将两个对象(其中一个对象持有对另一个对象的引用)传递到线程中

Passing two objects, where one holds a reference to another, into a thread

我有两个对象,其中第二个对象需要第一个对象比它长寿,因为它持有对第一个对象的引用。我需要将它们都移到一个线程中,但编译器抱怨第一个线程不够长。这是代码:

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

Playground

错误:

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 对象移动到线程,引用将不再有效。但是我无法找到解决这个问题的有效方法,假设我想保持结构完好无损。

I asked this question on the Rust forums as well

。让我们看一个简化的内存示例:

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),那么我们将访问未定义的内存!这正是 Rust 帮助防止的 class 个错误 - Rust 万岁!

如何修复取决于您的实际情况。在你的例子中,我建议 moving the facade into the thread,then create the RoutingNode on the reference在线程的堆栈中:

let facade = MyFacade;

let t = thread::spawn(move || {
    let f = facade;
    let r = RoutingNode::new(&f);
});

这是人们通常说的答案部分"but that demo code isn't what my real code does",所以我期待额外的复杂性!

unfortunately I can't use this solution as I need to use the routing object in the main thread prior to sending it to the other thread

我在这里看到了一些选项。最直接的方法是让包装对象 取得包装对象的所有权 ,而不仅仅是引用:

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

Another option is to use scoped threads。这允许您拥有一个可以从闭包外部引用的线程,但是 必须在借用的变量超出范围之前加入 。范围线程的两个潜在提供者:

使用横梁:

extern crate crossbeam;

let facade = MyFacade;
let routing = RoutingNode::new(&facade);

crossbeam::scope(|scope| {
    scope.spawn(|| {
        let r = routing;
    })
});

如果第一个选项对您的情况具有语义意义,我更喜欢第一个选项。我也喜欢第二个选项,因为线程的生命周期通常不需要是整个程序。