Day17:Thread & Closure - 100DayOfRust
Tags: rust
Published: 2019-12-03 05:01:29

First try, define a mutable vector then pass a closure to thread, inside closure, push another value to vector.

```rust
use std::{thread, time};

fn main() {
    let mut numbers = vec![1, 2, 3]; 
    let t1 = thread::spawn(|| {
        thread::sleep(time::Duration::from_secs(1));
        numbers.push(4);
        println!("numbers: {:?}", numbers);
    }); 
    t1.join().unwrap();
    println!("numbers: {:?}", numbers);
}
```

If we run the code, we got errors:
```
error[E0373]: closure may outlive the current function, but it borrows `numbers`, which is owned by the current function
 --> thread1.rs:5:28
  |
5 |     let t1 = thread::spawn(|| {
  |                            ^^ may outlive borrowed value `numbers`
6 |         thread::sleep(time::Duration::from_secs(1));
7 |         numbers.push(4);
  |         ------- `numbers` is borrowed here
...
5 |     let t1 = thread::spawn(move || {
  |                            ^^^^^^^

error[E0502]: cannot borrow `numbers` as immutable because it is also borrowed as mutable
...
10 |       t1.join().unwrap();
11 |       println!("numbers: {:?}", numbers);
   |                                 ^^^^^^^ immutable borrow occurs here
```

We got 2 errors here: the first one "closure may outlive the current function", this is because when we operate the `numbers` inside closure in a thread, the main thread might drop `numbers`, then the thread closure will refer to an invalid memory address. To solve this problem, we need to move the ownership of `numbers` to closure, such that Rust will know only the thread closure will use the `numbers`. Let's change the code:

```rust
...
let t1 = thread::spawn(move || {
...
```

Let's compile the code again, but still we have an error:
```
error[E0382]: borrow of moved value: `numbers`
  --> thread1.rs:11:31
   |
4  |     let mut numbers = vec![1, 2, 3];
   |         ----------- move occurs because `numbers` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
5  |     let t1 = thread::spawn(move || {
   |                            ------- value moved into closure here
6  |         thread::sleep(time::Duration::from_secs(1));
7  |         numbers.push(4);
   |         ------- variable moved due to use in closure
...
11 |     println!("numbers: {:?}", numbers);
   |                               ^^^^^^^ value borrowed here after move
```

Because we move the ownership of `numbers` to thread closure, the outside scope doesn't have the ownership of `numbers`, it cannot use the `numbers` anymore. Let's remove that line:

```rust
use std::{thread, time};

fn main() {
    let mut numbers = vec![1, 2, 3];
    let t1 = thread::spawn(move || {
        thread::sleep(time::Duration::from_secs(1));
        numbers.push(4);
        println!("numbers: {:?}", numbers);
    });
    t1.join().unwrap();
}
```

This time it can run:
```
numbers: [1, 2, 3, 4]
```

Let's try another example:
```rust
use std::{thread, time};

fn main() {
    let duration = time::Duration::from_secs(1);
    let t1 = thread::spawn(|| {
        thread::sleep(duration);
        println!("hello from spawned thread");
    });
    t1.join().unwrap();
    thread::sleep(duration);
    println!("hello from main thread");
}
```

The code has a similar logic like the first one, but inside the thread closure, instead of using vector, we used the Duration instance.

Compile it, we got an error:
```
error[E0373]: closure may outlive the current function, but it borrows `duration`, which is owned by the current function
 --> thread2.rs:5:28
  |
5 |     let t1 = thread::spawn(|| {
  |                            ^^ may outlive borrowed value `duration`
6 |         thread::sleep(duration);
  |                       -------- `duration` is borrowed here
  |
```

That is as expected, the closure using the duration in the outer scope, Rust suggested us to use the `move` keyword. Let's add it:

```rust
use std::{thread, time};

fn main() {
    let duration = time::Duration::from_secs(1);
    let t1 = thread::spawn(move || {
        thread::sleep(duration);
        println!("hello from spawned thread");
    });
    t1.join().unwrap();
    thread::sleep(duration);
    println!("hello from main thread");
}
```

Let's re-run the code:
```
hello from spawned thread
hello from main thread
```

It works! But wait, we are using `duration` not only in the thread closure, but also in the outer scope, why don't we have the error like in the first example? Remember in the first example, we are not allowed to use the `numbers` in the outer scope after we move the ownership to the thread closure. But in this example, we don't have such error and the code runs.

The reason is, for the `Duration` type, `move` won't really move the ownership, instead it will copy that variable to closure, such that both the thread closure and outer scope can use that variable.

So what `move` will do is:
* First check if this variable implemented the `Copy` trait, if the variable does, then `move` will do a variable copy and move the copy to the closure. In this case, the closure and outer scope can both use this variable.
* If this variable doesn't implement the `Copy` trait, `move` will move the ownership of that variable to thread closure. In this case, the outer scope cannot use the variable anymore.

To confirm this, if we dig into the source code of Duration type<sup>[1]</sup>, we can see:
```rust
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Duration {
    secs: u64,
    nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC
}
```

The magic happens in the code `#[derive(Clone, Copy)]`. We mentioned the `Copy` trait above, but what's this Clone? Well, `Clone` is also required, as it's a supertrait of `Copy`.<sup>[2]</sup>

Let's see another example, this time we are going to use our own struct.

```rust
use std::{thread, time};

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let duration = time::Duration::from_secs(1);
    let p = Point {x: 1, y:2};
    let t1 = thread::spawn(move || {
        thread::sleep(duration);
        println!("[Spawned thead] Point {:?}", p);
    });
    t1.join().unwrap();
    thread::sleep(duration);
    println!("[Main thead] Point {:?}", p);
}
```

We defined a struct called "Point" and created an instance `p`, then in thread closure, we used `move` and print out `p`, in the main thread, we also printed `p`.

Compile it we got the error:
```
error[E0382]: borrow of moved value: `p`
  --> thread3.rs:18:41
   |
11 |     let p = Point {x: 1, y:2};
   |         - move occurs because `p` has type `Point`, which does not implement the `Copy` trait
12 |     let t1 = thread::spawn(move || {
   |                            ------- value moved into closure here
13 |         thread::sleep(duration);
14 |         println!("[Spawned thead] Point {:?}", p);
   |                                                - variable moved due to use in closure
...
18 |     println!("[Main thead] Point {:?}", p);
   |                                         ^ value borrowed here after move

```

Review the `move` rule we wrote above: since `Point` doesn't implement the `Copy` trait, so `move` will move the ownership of `p`, then the outer scope cannot use the `p` anymore.

There are 2 ways to implement the `Copy` trait to our struct <sup>[3]</sup>, the easiest way if to use the `derive`, just like the one in the `Duration` source code. So change `#[derive(Debug)]` to `#[derive(Debug, Clone, Copy)]`. Here is the complete code:

```rust
use std::{thread, time};

#[derive(Debug, Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let duration = time::Duration::from_secs(1);
    let p = Point {x: 1, y:2};
    let t1 = thread::spawn(move || {
        thread::sleep(duration);
        println!("[Spawned thead] Point {:?}", p);
    });
    t1.join().unwrap();
    thread::sleep(duration);
    println!("[Main thead] Point {:?}", p);
}
```

Compile and run the code, it works!
```
[Spawned thead] Point Point { x: 1, y: 2 }
[Main thead] Point Point { x: 1, y: 2 }
```


### Reference
1. [Duration Type](https://doc.rust-lang.org/src/core/time.rs.html#54)
2. [Copy & Clone Trait](https://doc.rust-lang.org/std/marker/trait.Copy.html)
3. [How to implement Copy](https://doc.rust-lang.org/std/marker/trait.Copy.html#how-can-i-implement-copy)