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

时间:2015-04-08 21:54:27

标签: reference rust move-semantics lifetime 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();
}

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

1 个答案:

答案 0 :(得分:6)

The main problem is that once you have a reference to an item, you cannot move that item。我们来看一个简化的内存示例:

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有助于防止的一类错误 - 对于Rust来说很啰嗦!

如何解决这个问题取决于你的实际情况。在您的示例中,我建议将移动 facade到线程中,然后在线程堆栈中的引用上创建RoutingNode:< / p>

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

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

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