Передача двух объектов, где один содержит ссылку на другой, в поток
У меня есть два объекта, где второй требует, чтобы первый пережил его, потому что он содержит ссылку на первый. Мне нужно переместить их обоих в поток, но компилятор жалуется, что первый из них не живет достаточно долго. Вот код:
use std::thread;
trait Facade: Sync {
fn add(&self) -> u32;
}
struct RoutingNode<'a> {
facade: &'a (Facade + 'a),
}
impl<'a> RoutingNode<'a> {
fn new(facade: &'a Facade) -> RoutingNode<'a> {
RoutingNode { facade: facade }
}
}
fn main() {
struct MyFacade;
impl Facade for MyFacade {
fn add(&self) -> u32 {
999u32
}
}
let facade = MyFacade;
let routing = RoutingNode::new(&facade);
let t = thread::spawn(move || {
let f = facade;
let r = routing;
});
t.join();
}
И Ошибка:
error: `facade` does not live long enough
--> <anon>:27:37
|
27 | let routing = RoutingNode::new(&facade);
| ^^^^^^ does not live long enough
...
35 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
Я полагаю, что понимаю, о чем говорит мне ошибка: как только объект facade
перемещается в поток, ссылка больше не будет действительной. Но я не смог найти рабочего решения этой проблемы, предполагая, что я хотел бы сохранить структуры нетронутыми.
1 ответ:
Основная проблема заключается в том, что как только у вас есть ссылка на элемент, вы не можете переместить этот элемент. Давайте рассмотрим упрощенный пример памяти:
let a = Struct1; // the memory for Struct1 is on the stack at 0x1000 let b = &a; // the value of b is 0x1000 let c = a; // This moves a to c, and it now sits on the stack at 0x2000
О нет, если мы попытаемся использовать ссылку в
b
(которая все еще указывает на0x1000
), то мы получим доступ к неопределенной памяти! Это именно тот класс ошибок, которые ржавчина помогает предотвратить-ура ржавчине!Как это исправить, зависит от вашей реальной ситуации. В вашем примере я бы предложил переместить
facade
в поток, затем создайтеRoutingNode
по ссылке в стеке потока:let facade = MyFacade; let t = thread::spawn(move || { let f = facade; let r = RoutingNode::new(&f); });
Это та часть ответа, где люди обычно говорят :" но этот демонстрационный код не то, что мой реальный код", поэтому я с нетерпением жду дополнительной сложности!
К сожалению, я не могу использовать это решение, так как мне нужно использовать объект маршрутизации в главном потоке перед отправкой его в другой поток
Я вижу здесь несколько вариантов. Самое прямолинейное-это иметь объект обертывания принимает на себя ответственность обертываемого объекта, а не просто имеет ссылку:
use std::thread; trait Facade: Sync { fn add(&self) -> u32; } struct RoutingNode<F> { facade: F, } impl<F> RoutingNode<F> where F: Facade, { fn new(facade: F) -> RoutingNode<F> { RoutingNode { facade } } } fn main() { struct MyFacade; impl Facade for MyFacade { fn add(&self) -> u32 { 999u32 } } let facade = MyFacade; let routing = RoutingNode::new(facade); let t = thread::spawn(move || { let r = routing; }); t.join().expect("Unable to join"); }
Другой вариант-использовать потоки с областью действия . Это позволяет иметь поток, который может иметь ссылки из-за пределов замыкания, но должен быть соединен , прежде чем заимствованные переменные выйдут из области видимости. Два потенциальных поставщика ограниченных потоков:
Использование поперечная балка:
Я предпочитаю первый вариант, если он имеет смысл для вашей ситуации. Мне также нравится второй вариант, так как часто потоки имеют время жизни, которое не обязательно должно быть всей программой.extern crate crossbeam; let facade = MyFacade; let routing = RoutingNode::new(&facade); crossbeam::scope(|scope| { scope.spawn(|| { let r = routing; }) });