Skip to content

Commit 76ff835

Browse files
committed
Auto merge of #27503 - c-nixon:master, r=steveklabnik
I was reading through the docs and came across a section that felt awkward. I've tried to improve the flow by splitting up and reversing the explanations of Arc and Mutex with some example code in between. The "This would have have happened" bit is unfortunate but I couldn't see any other way to illustrate it. The compiler errors didn't really help tell the story in this particular instance so it still feels a bit forced. However I do think it's an a small improvement... Does anyone have any other ideas that might flow better?
2 parents 8228240 + d5b522e commit 76ff835

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)