如何将拥有的盒装结构引用给其他拥有的结构

How to give reference to owned boxed struct to other owned struct

我有 Engine,它拥有 Worker,我希望 EngineWorker 提供一些 API 作为特征的参考。 API 实现是使用 Box 分配的,并由 Engine 拥有,因此只要 worker 还活着,对它的引用就是稳定有效的。

但是我不明白用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();
}

错误:

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

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

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();
}

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();
}

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