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
697 views
in Technique[技术] by (71.8m points)

multithreading - 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().

See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

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

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

...