Skip to content

Commit d5b522e

Browse files
committed
Tweaked concurrency.md
1 parent c980aba commit d5b522e

File tree

1 file changed

+61
-33
lines changed

1 file changed

+61
-33
lines changed

src/doc/trpl/concurrency.md

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -135,28 +135,34 @@ This gives us an error:
135135
^~~~
136136
```
137137

138-
In this case, we know that our code _should_ be safe, but Rust isn't sure. And
139-
it's actually not safe: if we had a reference to `data` in each thread, and the
140-
thread takes ownership of the reference, we have three owners! That's bad. We
141-
can fix this by using the `Arc<T>` type, which is an atomic reference counted
142-
pointer. The 'atomic' part means that it's safe to share across threads.
138+
Rust knows this wouldn't be safe! If we had a reference to `data` in each
139+
thread, and the thread takes ownership of the reference, we'd have three
140+
owners!
141+
142+
So, we need some type that lets us have more than one reference to a value and
143+
that we can share between threads, that is it must implement `Sync`.
144+
145+
We'll use `Arc<T>`, rust's standard atomic reference count type, which
146+
wraps a value up with some extra runtime bookkeeping which allows us to
147+
share the ownership of the value between multiple references at the same time.
148+
149+
The bookkeeping consists of a count of how many of these references exist to
150+
the value, hence the reference count part of the name.
151+
152+
The Atomic part means `Arc<T>` can safely be accessed from multiple threads.
153+
To do this the compiler guarantees that mutations of the internal count use
154+
indivisible operations which can't have data races.
143155

144-
`Arc<T>` assumes one more property about its contents to ensure that it is safe
145-
to share across threads: it assumes its contents are `Sync`. But in our
146-
case, we want to be able to mutate the value. We need a type that can ensure
147-
only one person at a time can mutate what's inside. For that, we can use the
148-
`Mutex<T>` type. Here's the second version of our code. It still doesn't work,
149-
but for a different reason:
150156

151157
```ignore
152158
use std::thread;
153-
use std::sync::Mutex;
159+
use std::sync::Arc;
154160
155161
fn main() {
156-
let mut data = Mutex::new(vec![1, 2, 3]);
162+
let mut data = Arc::new(vec![1, 2, 3]);
157163
158164
for i in 0..3 {
159-
let data = data.lock().unwrap();
165+
let data = data.clone();
160166
thread::spawn(move || {
161167
data[i] += 1;
162168
});
@@ -166,29 +172,29 @@ fn main() {
166172
}
167173
```
168174

169-
Here's the error:
175+
We now call `clone()` on our `Arc<T>`, which increases the internal count.
176+
This handle is then moved into the new thread.
177+
178+
And... still gives us an error.
170179

171180
```text
172-
<anon>:9:9: 9:22 error: the trait `core::marker::Send` is not implemented for the type `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` [E0277]
173-
<anon>:11 thread::spawn(move || {
174-
^~~~~~~~~~~~~
175-
<anon>:9:9: 9:22 note: `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` cannot be sent between threads safely
176-
<anon>:11 thread::spawn(move || {
177-
^~~~~~~~~~~~~
181+
<anon>:11:24 error: cannot borrow immutable borrowed content as mutable
182+
<anon>:11 data[i] += 1;
183+
^~~~
178184
```
179185

180-
You see, [`Mutex`](../std/sync/struct.Mutex.html) has a
181-
[`lock`](../std/sync/struct.Mutex.html#method.lock)
182-
method which has this signature:
186+
`Arc<T>` assumes one more property about its contents to ensure that it is safe
187+
to share across threads: it assumes its contents are `Sync`. This is true for
188+
our value if it's immutable, but we want to be able to mutate it, so we need
189+
something else to persuade the borrow checker we know what we're doing.
183190

184-
```ignore
185-
fn lock(&self) -> LockResult<MutexGuard<T>>
186-
```
191+
It looks like we need some type that allows us to safely mutate a shared value,
192+
for example a type that that can ensure only one thread at a time is able to
193+
mutate the value inside it at any one time.
187194

188-
Because `Send` is not implemented for `MutexGuard<T>`, we can't transfer the
189-
guard across thread boundaries, which gives us our error.
195+
For that, we can use the `Mutex<T>` type!
190196

191-
We can use `Arc<T>` to fix this. Here's the working version:
197+
Here's the working version:
192198

193199
```rust
194200
use std::sync::{Arc, Mutex};
@@ -209,9 +215,31 @@ fn main() {
209215
}
210216
```
211217

212-
We now call `clone()` on our `Arc`, which increases the internal count. This
213-
handle is then moved into the new thread. Let's examine the body of the
214-
thread more closely:
218+
219+
If we'd tried to use `Mutex<T>` without wrapping it in an `Arc<T>` we would have
220+
seen another error like:
221+
222+
```text
223+
error: the trait `core::marker::Send` is not implemented for the type `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` [E0277]
224+
thread::spawn(move || {
225+
^~~~~~~~~~~~~
226+
note: `std::sync::mutex::MutexGuard<'_, collections::vec::Vec<u32>>` cannot be sent between threads safely
227+
thread::spawn(move || {
228+
^~~~~~~~~~~~~
229+
```
230+
231+
You see, [`Mutex`](../std/sync/struct.Mutex.html) has a
232+
[`lock`](../std/sync/struct.Mutex.html#method.lock)
233+
method which has this signature:
234+
235+
```ignore
236+
fn lock(&self) -> LockResult<MutexGuard<T>>
237+
```
238+
239+
and because `Send` is not implemented for `MutexGuard<T>`, we couldn't have
240+
transferred the guard across thread boundaries on it's own.
241+
242+
Let's examine the body of the thread more closely:
215243

216244
```rust
217245
# use std::sync::{Arc, Mutex};

0 commit comments

Comments
 (0)