Rust: Closures and Threads

2-minute read

Closures

Closures are a function that has access to variables in the same scope in which it is defined. Annotating types in closure definition are not required, types can be inferred by the compiler.

Closures in Threads

When using a closure, variables defined in the same scope as the closure, are accessible inside the closure. But the same accessibility changes when closure runs in threads. Let’s look at the examples below.

Here:

let mut str = "Hello".to_string();
let t = thread::spawn(|| {
    thread::sleep(time::Duration::from_secs(1));
    let mut str = format!("{:?}!", str);
    println!("str: {:?}", str);
});
t.join().unwrap();

We get the error: closure may outlive the current function because the main thread might drop str and the closure will not be able to access that variable.

To solve this we can use move which will move the ownership, see below:

let mut str = "Hello".to_string();
let t = thread::spawn(move || {
    thread::sleep(time::Duration::from_secs(1));
    let mut str = format!("{:?}!", str);
    println!("str: {:?}", str);
});
t.join().unwrap();

This will build and run fine since move has moved the ownership of str to the thread.

Now lets try to print str in the end in the main thread:

let mut str = "Hello".to_string();
let t = thread::spawn(move || {
    thread::sleep(time::Duration::from_secs(1));
    let mut str = format!("{:?}!", str);
    println!("str: {:?}", str);
});
t.join().unwrap();
println!("str: {:?}", str);

Here, the main thread is trying to access the variable which was moved to the thread, so the compiler gives an error here: does not implement the 'Copy' trait.


Lets look at another example:

let mut num = 20;
let t = thread::spawn(move || {
    thread::sleep(time::Duration::from_secs(1));
    let mut num = num + 1;
    println!("num: {:?}", num);
});
t.join().unwrap();
println!("num: {:?}", num);

Here, we get the output as:

num: 21
num: 20

This works because i32 implements copy trait and hence variable in the thread gets copied.


So while using a closure in threads, we need to take care that:

  • move will move the ownership of the variable to the thread.
  • variables that need to be accessed inside closure and in the main thread, must implement Copy trait so that move will copy the variables.