diff --git a/src/ch16-03-shared-state.md b/src/ch16-03-shared-state.md index ea6e4814b0..da46f215d5 100644 --- a/src/ch16-03-shared-state.md +++ b/src/ch16-03-shared-state.md @@ -102,10 +102,9 @@ to change the inner `i32` to 6. Now, let’s try to share a value between multiple threads using `Mutex`. We’ll spin up 10 threads and have them each increment a counter value by 1, so -the counter goes from 0 to 10. Note that the next few examples will have -compiler errors, and we’ll use those errors to learn more about using -`Mutex` and how Rust helps us use it correctly. Listing 16-13 has our -starting example: +the counter goes from 0 to 10. The next example in Listing 16-13 will have +a compiler error, and we’ll use that error to learn more about using +`Mutex` and how Rust helps us use it correctly. Filename: src/main.rs @@ -153,110 +152,23 @@ program. We hinted that this example wouldn’t compile. Now let’s find out why! ```text -error[E0382]: capture of moved value: `counter` - --> src/main.rs:10:27 - | -9 | let handle = thread::spawn(move || { - | ------- value moved (into closure) here -10 | let mut num = counter.lock().unwrap(); - | ^^^^^^^ value captured here after move - | - = note: move occurs because `counter` has type `std::sync::Mutex`, - which does not implement the `Copy` trait - error[E0382]: use of moved value: `counter` - --> src/main.rs:21:29 + --> src/main.rs:9:36 | 9 | let handle = thread::spawn(move || { - | ------- value moved (into closure) here -... -21 | println!("Result: {}", *counter.lock().unwrap()); - | ^^^^^^^ value used here after move - | - = note: move occurs because `counter` has type `std::sync::Mutex`, - which does not implement the `Copy` trait - -error: aborting due to 2 previous errors -``` - -The error message states that the `counter` value is moved into the closure and -then captured when we call `lock`. That description sounds like what we wanted, -but it’s not allowed! - -Let’s figure this out by simplifying the program. Instead of making 10 threads -in a `for` loop, let’s just make two threads without a loop and see what -happens. Replace the first `for` loop in Listing 16-13 with this code instead: - -```rust,ignore,does_not_compile -use std::sync::Mutex; -use std::thread; - -fn main() { - let counter = Mutex::new(0); - let mut handles = vec![]; - - let handle = thread::spawn(move || { - let mut num = counter.lock().unwrap(); - - *num += 1; - }); - handles.push(handle); - - let handle2 = thread::spawn(move || { - let mut num2 = counter.lock().unwrap(); - - *num2 += 1; - }); - handles.push(handle2); - - for handle in handles { - handle.join().unwrap(); - } - - println!("Result: {}", *counter.lock().unwrap()); -} -``` - -We make two threads and change the variable names used with the second thread -to `handle2` and `num2`. When we run the code this time, compiling gives us the -following: - -```text -error[E0382]: capture of moved value: `counter` - --> src/main.rs:16:24 - | -8 | let handle = thread::spawn(move || { - | ------- value moved (into closure) here -... -16 | let mut num2 = counter.lock().unwrap(); - | ^^^^^^^ value captured here after move - | - = note: move occurs because `counter` has type `std::sync::Mutex`, - which does not implement the `Copy` trait - -error[E0382]: use of moved value: `counter` - --> src/main.rs:26:29 - | -8 | let handle = thread::spawn(move || { - | ------- value moved (into closure) here -... -26 | println!("Result: {}", *counter.lock().unwrap()); - | ^^^^^^^ value used here after move + | ^^^^^^^ value moved into closure here, +in previous iteration of loop +10 | let mut num = counter.lock().unwrap(); + | ------- use occurs due to use in closure | = note: move occurs because `counter` has type `std::sync::Mutex`, - which does not implement the `Copy` trait - -error: aborting due to 2 previous errors +which does not implement the `Copy` trait ``` -Aha! The first error message indicates that `counter` is moved into the closure -for the thread associated with `handle`. That move is preventing us from -capturing `counter` when we try to call `lock` on it and store the result in -`num2` in the second thread! So Rust is telling us that we can’t move ownership -of `counter` into multiple threads. This was hard to see earlier because our -threads were in a loop, and Rust can’t point to different threads in different -iterations of the loop. Let’s fix the compiler error with a multiple-ownership -method we discussed in Chapter 15. +The error message states that the `counter` value was moved in the previous +iteration of the loop. So Rust is telling us that we can’t move the ownership +of lock `counter` into multiple threads. Let’s fix the compiler error with a +multiple-ownership method we discussed in Chapter 15. #### Multiple Ownership with Multiple Threads @@ -303,30 +215,27 @@ Once again, we compile and get... different errors! The compiler is teaching us a lot. ```text -error[E0277]: the trait bound `std::rc::Rc>: -std::marker::Send` is not satisfied in `[closure@src/main.rs:11:36: -15:10 counter:std::rc::Rc>]` +error[E0277]: `std::rc::Rc>` cannot be sent between threads safely --> src/main.rs:11:22 | 11 | let handle = thread::spawn(move || { | ^^^^^^^^^^^^^ `std::rc::Rc>` cannot be sent between threads safely | - = help: within `[closure@src/main.rs:11:36: 15:10 -counter:std::rc::Rc>]`, the trait `std::marker::Send` is -not implemented for `std::rc::Rc>` + = help: within `[closure@src/main.rs:11:36: 14:10 +counter:std::rc::Rc>]`, the trait `std::marker::Send` +is not implemented for `std::rc::Rc>` = note: required because it appears within the type -`[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc>]` +`[closure@src/main.rs:11:36: 14:10 counter:std::rc::Rc>]` = note: required by `std::thread::spawn` ``` -Wow, that error message is very wordy! Here are some important parts to focus -on: the first inline error says `` `std::rc::Rc>` cannot -be sent between threads safely ``. The reason for this is in the next important -part to focus on, the error message. The distilled error message says `` the -trait bound `Send` is not satisfied ``. We’ll talk about `Send` in the next -section: it’s one of the traits that ensures the types we use with threads are -meant for use in concurrent situations. +Wow, that error message is very wordy! Here’s the important part to focus +on: `` `Rc>` cannot be sent between threads safely ``. The compiler +is also telling us the reason why: ``the trait `Send` is not implemented for +`Rc>` ``. We’ll talk about `Send` in the next section: it’s one of +the traits that ensures the types we use with threads are meant for use in +concurrent situations. Unfortunately, `Rc` is not safe to share across threads. When `Rc` manages the reference count, it adds to the count for each call to `clone` and