Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Joining a thread in a method that takes `&mut self` (like drop) results in "cannot move out of borrowed content"

I want to create a thread inside of the new method and stop it after the struct is destroyed:

use std::thread;

struct Foo {
    handle: thread::JoinHandle<()>,
}

impl Foo {
    pub fn new(name: &str) -> Foo {
        let name = name.to_string();
        Foo {
            handle: thread::spawn(move || {
                println!("hi {}", name);
            }),
        }
    }
    pub fn stop(&mut self) {
        self.handle.join();
    }
}

fn main() {
    let mut foo = Foo::new("test");
    foo.stop();
}

This doesn't compile, and I can not understand why:

error[E0507]: cannot move out of borrowed content
  --> <anon>:15:9
   |
15 |         self.handle.join();
   |         ^^^^ cannot move out of borrowed content

And in newer versions of Rust:

error[E0507]: cannot move out of `self.handle` which is behind a mutable reference
  --> src/main.rs:17:9
   |
17 |         self.handle.join();
   |         ^^^^^^^^^^^ move occurs because `self.handle` has type `std::thread::JoinHandle<()>`, which does not implement the `Copy` trait

How can I fix this error?

In the future, I will implement Drop for Foo, and will call stop() from drop().

like image 208
user1244932 Avatar asked Dec 26 '16 12:12

user1244932


People also ask

What is the join () method in Java?

What is a Join Method in Java? Join method in Java allows one thread to wait until another thread completes its execution. In simpler words, it means it waits for the other thread to die. It has a void type and throws InterruptedException.

What is the difference between wait () and join ()?

The wait() is used for inter-thread communication while the join() is used for adding sequencing between multiple threads, one thread starts execution after first thread execution finished.

What is the difference between wait () and sleep () method?

Wait() method releases lock during Synchronization. Sleep() method does not release the lock on object during Synchronization. Wait() should be called only from Synchronized context. There is no need to call sleep() from Synchronized context.

Why would threads call the join () function on thread?

join() a ThreadTo tell one thread to wait for another thread to finish, you call . join() . If you uncomment that line, the main thread will pause and wait for the thread x to complete running.


1 Answers

The function signature of JoinHandle::join is:

fn join(self) -> Result<T>

This means that the method takes self (the receiver object) by values (taking the ownership/consuming it). But you only have a borrow to your JoinHandle; a mutable one, but still merely a borrow, not the ownership. Thus you can't call this method, because you can't move the ownership out of your borrow into this join() method.

An easy way to fix that, is by accepting self by value in the stop() method, too:

pub fn stop(self) {
    self.handle.join();
}

But you will notice that this isn't possible when implementing Drop, because drop() has the signature fn drop(&mut self)! Bummer! But there is a little trick you can use, described below. Please be aware that joining threads in drop() is probably not a good idea! Read Matthieu M.'s answer for more information on that!

If you still think, for whatever reason, that you really want to join a thread in drop(), you can store the JoinHandle in an Option<T> to save whether or not it's already joined. If you have a Some(T) you can obtain a T (by value!) from it by using the method Option::take(). Then you can write:

fn drop(&mut self) {
    // `self.handle` has the type `Option<JoinHandle<()>>` here!
    if let Some(handle) = self.handle.take() {
        handle.join().expect("failed to join thread");
    }
}
like image 147
Lukas Kalbertodt Avatar answered Nov 16 '22 02:11

Lukas Kalbertodt