Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
540 views
in Technique[技术] by (71.8m points)

rust - Borrowed value does not live long enough with Arc, thread and channel

the target of my code is to send custom Data with channel (std::sync::mpsc::channel) in the thread. Data instance are created by a Manager and have a reference on Manager.

use std::sync::mpsc;
use std::sync::Arc;
use std::thread;

struct Manager {}

struct Data<'a> {
    manager: &'a Manager,
}

impl Manager {
    fn new() -> Self {
        Self {}
    }

    fn make(&self) -> Data {
        Data { manager: self }
    }
}

unsafe impl Send for Manager {}

fn main() {
    let manager = Arc::new(Manager::new());
    let (tx, _rx) = mpsc::channel();
    let mut children = Vec::new();

    for id in 0..2 {        
        let thread_manager = Arc::clone(&manager);
        let thread_tx = tx.clone();

        let child = thread::spawn(move || {
            let msg = thread_manager.make();
            thread_tx.send(msg).unwrap();
            println!("thread {} finished", id);
        });
        children.push(child);
    }
}

playground

And i have an error

error[E0597]: `thread_manager` does not live long enough
  --> src/main.rs:36:23
   |
33 |         let thread_tx = tx.clone();
   |             --------- lifetime `'1` appears in the type of `thread_tx`
...
36 |             let msg = thread_manager.make();
   |                       ^^^^^^^^^^^^^^ borrowed value does not live long enough
37 |             thread_tx.send(msg).unwrap();
   |             ------------------- argument requires that `thread_manager` is borrowed for `'1`
38 |             println!("thread {} finished", id);
39 |         });
   |         - `thread_manager` dropped here while still borrowed

But i don't understand :

  1. let manager = Arc::new(Manager::new()); create an instance of Manager with ref counter
  2. let thread_manager = Arc::clone(&manager); increase by 1 ref counter but it is the same Managerinstance
  3. let msg = thread_manager.make(); create a Data with a reference on Manager

For me, msg could have same lifetime as manager.

where am I wrong ?

Thanks Etienne

question from:https://stackoverflow.com/questions/65641458/borrowed-value-does-not-live-long-enough-with-arc-thread-and-channel

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

For me, msg could have same lifetime as manager.

Yes. That means msg lives as long as the enclosing function does:

let child = thread::spawn(move || {
    // <— thread_manager moved into and owned by the function
    let msg = thread_manager.make();
    thread_tx.send(msg).unwrap();
    println!("thread {} finished", id);
    // <— thread_manager is dropped here so `msg` becomes invalid
});

there's no telling when the message will actually be received / used, but it'll likely be after the thread has died. And rustc can't assume any other instance of the manager will remain alive at this point, so it has to assume it'll be left with a dangling reference.

Why doesn't msg just use an Arc<Manager>, or even a Weak<Manager>? (though the latter doesn't seem like a great idea here).

You might be able to do something along those lines of what you're attempting (without even Arc I believe) using scoped threads, but the stdlib's are not scoped, as far as the compiler is concerned there is no lifetime relationship between the caller and contents of a spawn call, they live completely independent lives, and can run and stop in any order (even if you use join). So the reasoning about thread-bound lifetimes is completely local.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...