2016-03-14 6 views
1

У меня есть Engine, которому принадлежит Worker, и я хочу, чтобы Engine предоставил некоторый API до Worker в качестве ссылки на черту. Реализация API распределяется с использованием Box и принадлежит Engine, поэтому ссылка на нее является стабильной и действительной, пока рабочий жив.Как указать ссылку на принадлежащую коробке структуру другому собственному структурному объекту

Но я не понимаю, как выразить это в Rust.

Я прочитал Why can't I store a value and a reference to that value in the same struct?, и я понимаю, почему я не могу передать ссылку на принадлежащую ему стоимость. Однако, в моем случае, я передаю ссылку не на собственную ценность, а на значение в коробке, которое не будет перемещено, поэтому ссылка на него должна быть стабильной.

Здесь нерабочий прототип:

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 
+0

Возможный дубликат [Почему я не могу сохранить значение и ссылку на это значение в той же структуре?] (Http://stackoverflow.com/questions/32300132/why-cant-i-store-a- value-and-a-reference-to-that-value-in-the-same-struct) –

+0

Я прочитал этот вопрос, и я понимаю, почему я не могу хранить ссылку на принадлежащее значение - поскольку значение перемещается при сохранении в struct и позже, когда сама структура перемещается. Однако в моем случае значение вставляется в коробку, поэтому ссылка стабильна и, хотя она может быть не выражена в Rust, она должна быть действительной. – kriomant

+0

В этом случае вы могли (1) ссылаться на эту ссылку в своем вопросе и (2) объяснить в своем вопросе, что вы поняли, и почему вы считаете, что ваш случай отличается? Это поможет нам понять, что именно объяснить. –

ответ

2

Проблема заключается в том, что в вашем примере, нет ничего связывание 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(); 
} 

Компилятор Ржавчины не может использовать тот факт, что объект должен быть подвижным, так как все это ссылки хранятся в куче и жить как минимум до тех пор, пока объект. Может быть, когда-нибудь в будущем. На данный момент вам приходится прибегать к небезопасному коду.

+0

WoW. Эта самоблокировка - это просто волшебство. Мне потребовалось немного понять разницу в поведении между «turn_on» (где я могу перемещать движок в любом месте) и после 'turn_on' (где это неподвижно), и я все еще не уверен, что все детали отсортированы. Тот факт, что это неподвижно впоследствии может быть болезненным, хотя (нет 'turn_off'). –

+0

Да, эта самоблокировка - действительно классный трюк. Тем не менее, мне не нравится двухэтапная инициализация, поэтому я, вероятно, прибегаю к совместному использованию API через Rc. – kriomant

 Смежные вопросы

  • Нет связанных вопросов^_^