Calling instance function within threads

I'm fairly new to RUST. I have the following example, where I don't understand why you can call an instance function in a thread, but then can't call another instance function in the first instance function.

Code example:

use std::thread;

struct Greeter {
    name: String,
    i:  u32,
}

impl Greeter {
    fn greet(&self, i: u32 ) {
        println!("Hello from {}", self.name);
        println!("Number is: {}", i );
        let handle = thread::spawn( move || {
            self.print(); // `greeter` is moved into the thread, so we can call its method
    });
        
        handle.join().unwrap();
    }
    
    fn print(&self) {
        println!("In PRINT function.");
    }
}

fn main() {
    let greeter = Greeter {
        name: "MyName".to_string(),
        i: 1,
    };

        greeter.greet( 32 ); // `greeter` is moved into the thread, so we can call its method

}

Here is the error I get:

 Compiling playground v0.0.1 (/playground)
error[E0521]: borrowed data escapes outside of method
  --> src/main.rs:12:14
   |
9  |   fn greet(&self, i: u32 ) {
   |            -----
   |            |
   |            `self` is a reference that is only valid in the method body
   |            let's call the lifetime of this reference `'1`
...
12 |   let handle = thread::spawn( move || {
   |  ______________^
13 | | self.print(); // greeter is moved into the thread, so we can call its method
14 | | });
   | |  ^
   | |  |
   | |__`self` escapes the method body here
   |    argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` (bin "playground") due to 1 previous error

This pattern, i.e. spawning a thread and immediately joining it, doesn't allow you to capture non-'static items in the new thread, even though the spawned thread doesn't escape the function body of Greeter::greet and thus can't actually cause use-after-frees. But the compiler can't reason like this. Instead, the standard library provides a different API, which does allow capturing non-'static data in the spawned thread, std::thread::scope:

use std::thread;

struct Greeter {
    name: String,
    i: u32,
}

impl Greeter {
    fn greet(&self, i: u32) {
        println!("Hello from {}", self.name);
        println!("Number is: {}", i);

        thread::scope(|s| {
            s.spawn(|| {
                self.print();
            });
        }); // implicit join here
    }

    fn print(&self) {
        println!("In PRINT function.");
    }
}

fn main() {
    let greeter = Greeter {
        name: "MyName".to_string(),
        i: 1,
    };

    greeter.greet(32); // `greeter` is moved into the thread, so we can call its method
}

Playground.

3 Likes

Thanks @jofas! I am now past that issue :grinning_face:!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.