如何将拥有的装箱结构引用到其他拥有的结构

kri*_*ant 5 lifetime rust

我拥有Engine它,Worker并且我想Engine提供一些 API 作为Worker对特征的引用。API 实现是使用 分配Box并由 拥有的Engine,因此只要工作进程还活着,对它的引用就是稳定且有效的。

但我不明白如何用 Rust 表达它。

我已阅读为什么不能在同一结构中存储值和对该值的引用?我明白为什么我不能传递对拥有价值的引用。但是,就我而言,我传递的不是对拥有值本身的引用,而是对装箱值的引用,该值不会被移动,因此对它的引用必须是稳定的。

这是非工作原型:

trait EngineApi {
    fn foo(&self);
}

struct Worker<'a> {
    api: &'a EngineApi,
}
impl<'a> Worker<'a> {
    fn new(engine_api: &'a EngineApi) -> Self {
        Worker { api: engine_api }
    }
}

struct Api;
impl EngineApi for Api {
    fn foo(&self) {} 
}

struct Engine<'a> {
    api: Box<Api>,
    worker: Box<Worker<'a>>,
}

impl<'a> Engine<'a> {
    fn new() -> Self {
        let api = Box::new(Api);
        let worker = Box::new(Worker::new(api.as_ref()));
        Engine { api: api, worker: worker }
    }
}

fn main() {
    let engine = Engine::new();
}
Run Code Online (Sandbox Code Playgroud)

错误:

test.rs:27:37: 27:40 error: `api` does not live long enough
test.rs:27      let worker = Box::new(Worker::new(api.as_ref()));
                                                  ^~~
test.rs:25:19: 29:3 note: reference must be valid for the lifetime 'a as defined on the block at 25:18...
test.rs:25  fn new() -> Self {
test.rs:26      let api = Box::new(Api);
test.rs:27      let worker = Box::new(Worker::new(api.as_ref()));
test.rs:28      Engine { api: api, worker: worker }
test.rs:29  }
test.rs:26:27: 29:3 note: ...but borrowed value is only valid for the block suffix following statement 0 at 26:26
test.rs:26      let api = Box::new(Api);
test.rs:27      let worker = Box::new(Worker::new(api.as_ref()));
test.rs:28      Engine { api: api, worker: worker }
test.rs:29  }
error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)

oli*_*obk 3

问题是,在您的示例中,没有任何东西可以将对象绑定api到比它创建的范围更长的寿命。所以基本上您需要首先创建整个引擎对象,然后 Rust 可以推断这些生命周期。但是,如果不填写所有字段,就无法安全地创建对象。但您可以将该worker字段更改为 anOption并稍后填写:

struct Engine<'a> {
    api: Box<Api>,
    worker: Option<Box<Worker<'a>>>,
}

impl<'a> Engine<'a> {
    fn new() -> Self {
        let api = Box::new(Api);
        Engine { api: api, worker: None }
    }
    fn turn_on(&'a mut self) {
        self.worker = Some(Box::new(Worker::new(self.api.as_ref())));
    }
}

fn main() {
    let mut engine = Engine::new();
    engine.turn_on();
}
Run Code Online (Sandbox Code Playgroud)

调用engine.turn_on()将锁定对象以确保它将保留在范围内。那么你甚至不需要盒子来确保安全,因为该物体将变得不可移动:

struct Engine<'a> {
    api: Api,
    worker: Option<Worker<'a>>,
}

impl<'a> Engine<'a> {
    fn new() -> Self {
        let api = Api;
        Engine { api: api, worker: None }
    }
    fn turn_on(&'a mut self) {
        self.worker = Some(Worker::new(&self.api));
    }
}

fn main() {
    let mut engine = Engine::new();
    engine.turn_on();
}
Run Code Online (Sandbox Code Playgroud)

Rust 编译器不能利用对象应该是可移动的这一事实,因为它引用的东西存储在堆上并且至少与对象一样长。也许在未来的某一天。现在你必须求助于不安全的代码。