Передача двух объектов, где один содержит ссылку на другой, в поток


У меня есть два объекта, где второй требует, чтобы первый пережил его, потому что он содержит ссылку на первый. Мне нужно переместить их обоих в поток, но компилятор жалуется, что первый из них не живет достаточно долго. Вот код:

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 перемещается в поток, ссылка больше не будет действительной. Но я не смог найти рабочего решения этой проблемы, предполагая, что я хотел бы сохранить структуры нетронутыми.

Я задавал этот вопрос и на форумах Rust

1   5  

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;
    })
});
Я предпочитаю первый вариант, если он имеет смысл для вашей ситуации. Мне также нравится второй вариант, так как часто потоки имеют время жизни, которое не обязательно должно быть всей программой.