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

自闭症网瘾萝莉.ら 提交于 2021-02-20 11:51:10

问题


I have two objects where the second one requires the fist one to outlive it because it holds a reference to the first one. I need to move both of them into a thread, but the compiler is complaining that the first one doesn't live long enough. Here is the code:

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

And the error:

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...

I believe I understand what the error is telling me: that once the facade object is moved to the thread, the reference will no longer be valid. But I was unable to find a working solution to this problem, assuming I would like to keep the structures intact.

I asked this question on the Rust forums as well


回答1:


The main problem is that once you have a reference to an item, you cannot move that item. Let's look at a simplified example of memory:

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

Oh no, if we try to use the reference in b (which still points at 0x1000), then we will access undefined memory! This is exactly a class of bug that Rust helps prevent - hooray for Rust!

How to fix it depends on your actual situation. In your example, I'd suggest moving the facade into the thread, then create the RoutingNode on the reference in the thread's stack:

let facade = MyFacade;

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

This is the part of the answer where people usually say "but that demo code isn't what my real code does", so I look forward to the extra complexity!

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

I see a few options here. The most straight-forward is to have the wrapping object take ownership of the wrapped object, and not just have a reference:

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. This allows you to have a thread that can have references from outside the closure, but must be joined before the borrowed variables go out of scope. Two potential providers of scoped threads:

  • crossbeam
  • scoped-threadpool

Using crossbeam:

extern crate crossbeam;

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

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

I prefer the first option if it makes semantic sense for your situation. I also like the second option, as often threads have a lifetime that doesn't need to be the entire program.



来源:https://stackoverflow.com/questions/29526058/passing-two-objects-where-one-holds-a-reference-to-another-into-a-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!