Why can't use a thread handle which stored in a struct?

I want to implement an executor. Here I should store the thread handle in a struct then I can join it for waiting threads to stop friendly. But it errors when I prepare to invoke the join() method. I would like to know the reason and how to fix that. The codes are below:

struct Executor{
    tx:Sender<Box<dyn Send + Fn()>>,
    t:JoinHandle<()>,
}
impl Executor {
    fn new()->Self{
        let (tx, rx) = mpsc::channel::<Box<dyn Send + Fn()>>();
        let handle = thread::spawn(move || {
            loop{
                //TODO: check stop flag
                rx.recv().unwrap()();
            }
        });
       
        let ins = Executor{tx:tx, t:handle};
        ins
    }
    fn sender(&self)->Sender<Box<dyn Send + Fn()>>{
        self.tx.clone()
    }
    fn join(&self){
        self.t.join().unwrap();
    }
}

the errors:

error[E0507]: cannot move out of `self.t` which is behind a shared reference
  --> src\main.rs:77:9
   |
77 |         self.t.join().unwrap();
   |         ^^^^^^ move occurs because `self.t` has type `JoinHandle<()>`, which does not implement the `Copy` trait

This is because the JoinHandle::join method takes ownership of the JoinHandle, destroying it in the process. One option is to put it in an Option:

use std::sync::mpsc::Sender;
use std::thread::JoinHandle;

struct Executor {
    tx: Sender<Box<dyn Send + Fn()>>,
    t: Option<JoinHandle<()>>,
}
impl Executor {
    fn join(&mut self) {
        if let Some(handle) = self.t.take() {
            handle.join().unwrap();
        }
    }
}

With this code, first time you call join it will take ownership of the handle, replacing the Option with None and wait for it to exit. Future calls will return immediately since the Option is None.

2 Likes