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

Pet*_*iak 5 reference lifetime move-semantics rust lifetime-scoping

我有两个对象,第二个对象要求第一个对象比它更长,因为它包含对第一个对象的引用.我需要将它们都移动到一个线程中,但编译器抱怨第一个没有足够长的时间.这是代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

操场

而错误:

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...
Run Code Online (Sandbox Code Playgroud)

我相信我理解错误告诉我的:一旦将facade对象移动到线程,引用将不再有效.但我无法找到解决这个问题的工作方案,假设我想保持结构完好无损.

我也在Rust论坛上问过这个问题

She*_*ter 6

主要问题是,一旦您有一个项目的引用,您就无法移动该项目.我们来看一个简化的内存示例:

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
Run Code Online (Sandbox Code Playgroud)

哦不,如果我们尝试使用引用b(仍然指向0x1000),那么我们将访问未定义的内存!这正是Rust帮助防止的一类错误 - 对于Rust来说是万岁!

如何解决这个问题取决于你的实际情况.在你的榜样,我建议你移动facade进线,然后创建RoutingNode在线程的堆栈参考:

let facade = MyFacade;

let t = thread::spawn(move || {
    let f = facade;
    let r = RoutingNode::new(&f);
});
Run Code Online (Sandbox Code Playgroud)

这是答案的一部分,人们通常说"但是演示代码不是我真正的代码所做的",所以我期待更多的复杂性!

遗憾的是我无法使用此解决方案,因为我需要在将主要线程中的路由对象发送到其他线程之前使用它

我在这里看到一些选择.最直接的是让包装对象获得包装对象的所有权,而不仅仅是引用:

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");
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用范围线程.这允许您拥有一个可以从闭包外部引用的线程,但必须在借用的变量超出范围之前连接.两个潜在的作用域线程提供者:

使用横梁:

extern crate crossbeam;

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

crossbeam::scope(|scope| {
    scope.spawn(|| {
        let r = routing;
    })
});
Run Code Online (Sandbox Code Playgroud)

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