Remove review comments

This commit is contained in:
Carol (Nichols || Goulding)
2018-01-21 12:42:44 -05:00
parent b27852e47b
commit d665ddf76f
5 changed files with 4 additions and 274 deletions

View File

@@ -25,24 +25,12 @@ shipped to production. Weve nicknamed this aspect of Rust *fearless
concurrency*. Fearless concurrency allows you to write code thats free of
subtle bugs and is easy to refactor without introducing new bugs.
<!-- Can you say explicitly why making concurrency issues compile time errors
rather than runtime errors is an advantage? -->
<!-- I feel like we've explained this a few times now, but I suppose since the
advantage should be greater in concurrent code it's worth saying again /Carol
-->
> Note: well be referring to many of the problems here as *concurrent* rather
> than being more precise by saying *concurrent and/or parallel*, for
> simplicitys sake. If this were a book specifically about concurrency and/or
> parallelism, wed be sure to be more specific. For this chapter, please
> mentally substitute *concurrent and/or parallel* whenever we say *concurrent*.
<!-- I'm not sure what you mean about languages being strongly opinionated over
these issues and what kind of strategy that is, below, can you be more
specific? -->
<!-- I've added an example and elaborated on the strategy we're talking about
here. /Carol -->
Many languages are strongly opinionated about the solutions they offer for
dealing with concurrent problems. For example, Erlang has elegant functionality
for message passing concurrency, but only obscure ways to share state between
@@ -71,10 +59,6 @@ In most operating systems today, an executed programs code is run in a
your program, you can also have independent parts that run simultaneously. The
feature that runs these independent parts is called *threads*.
<!-- I've tried to simplify the text above, can you check that I haven't
changed meaning? -->
<!-- Made some small tweaks, overall seems fine /Carol -->
Splitting the computation in your program up into multiple threads can improve
performance, since the program will be doing multiple things at the same time,
but it also adds complexity. Because threads may run simultaneously, theres no
@@ -88,10 +72,6 @@ threads will run. This can lead to problems such as:
- Bugs that only happen in certain situations and are hard to reproduce and
fix reliably
<!-- How do threads prevent each other from continuing? Or is that something
we'll cover later?-->
<!-- We don't really get into that later, so I've expanded a bit here /Carol -->
Rust attempts to mitigate negative effects of using threads. Programming in a
multithreaded context still takes careful thought and requires a code structure
thats different from programs that run in a single thread.
@@ -112,13 +92,6 @@ Each model has its own advantages and tradeoffs, and the tradeoff most
important to Rust is runtime support. *Runtime* is a confusing term and can
have different meanings in different contexts.
<!-- Below - you mean this is the cause of runtime? Or "runtime" literally
means the code included by Rust in every binary? -->
<!-- Runtime literally means the code included by Rust in every binary.
Wikipedia calls this "runtime system":
https://en.wikipedia.org/wiki/Runtime_system but most people colloquially just
say "the runtime". I've tried to clarify. /Carol -->
In this context, by runtime we mean code thats included by the language in
every binary. This code can be large or small depending on the language, but
every non-assembly language will have some amount of runtime code. For that
@@ -185,10 +158,6 @@ hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
```
<!-- This seems interesting, how come the threads often take turns, but not
always? -->
<!-- I've added a bit of clarification /Carol -->
The threads will probably take turns, but thats not guaranteed: it depends on
how your operating system schedules the threads. In this run, the main thread
printed first, even though the print statement from the spawned thread appears
@@ -206,10 +175,6 @@ the time, because the main thread ends before the spawned thread is done,
theres actually no guarantee that the spawned thread will get to run at all,
because theres no guarantee on the order in which threads run!
<!-- Above -- why is this the case, because there are no guarantees over which
order the threads run in? -->
<!-- Yep! /Carol -->
We can fix this by saving the return value of `thread::spawn` in a variable.
The return type of `thread::spawn` is `JoinHandle`. A `JoinHandle` is an owned
value that, when we call the `join` method on it, will wait for its thread to
@@ -217,11 +182,6 @@ finish. Listing 16-2 shows how to use the `JoinHandle` of the thread we created
in Listing 16-1 and call `join` in order to make sure the spawned thread
finishes before the `main` exits:
<!-- Saving the return value where? I think this explanation of join handle
needs expanding, this feels cut short -->
<!-- In a variable. I've expanded a bit, but I'm not sure what information
seems missing, so I'm not sure if this is sufficient /Carol -->
Filename: src/main.rs
```
@@ -251,10 +211,6 @@ thread is prevented from performing work or exiting. Because weve put the cal
to `join` after the main threads `for` loop, running this example should
produce output that looks something like this:
<!-- Liz: I've added a definition of "block" in the context of threads here,
which is the first time we used the term-- it seemed to cause some confusion
later on. /Carol -->
```
hi number 1 from the main thread!
hi number 2 from the main thread!
@@ -327,8 +283,6 @@ another thread.
In Chapter 13, we said that “Creating closures that capture values from their
environment is mostly used in the context of starting new threads.”
<!-- PROD: DE to check this quote, see if it has changed -->
Now were creating new threads, so lets talk about capturing values in
closures!
@@ -448,9 +402,6 @@ fn main() {
Listing 16-5: Using the `move` keyword to force a closure to take ownership of
the values it uses
<!-- Can you be more specific about the question we're asking about 16-4?-->
<!-- Done /Carol -->
What would happen to the code in Listing 16-4 where the main thread called
`drop` if we use a `move` closure? Would `move` fix that case? Nope, we get a
different error, because what Listing 16-4 is trying to do isnt allowed for a
@@ -482,11 +433,6 @@ rules when we try to use `v` in the main thread. The `move` keyword overrides
Rusts conservative default of borrowing; it doesnt let us violate the
ownership rules.
<!-- Uh oh, I'm lost again, I thought we were trying to fix 16-4 with move, but
we don't want it to work, is that right? Can you talk about this a little?-->
<!-- I've tried to clarify a bit in the paragraph before this error and a bit
after the error /Carol -->
Now that we have a basic understanding of threads and the thread API, lets
talk about what we can actually *do* with threads.
@@ -502,9 +448,6 @@ documentation:
>
> --Effective Go at *http://golang.org/doc/effective_go.html*
<!-- below -- what is the channel, precisely? A crate? a technique?-->
<!-- I've elaborated /Carol -->
One major tool Rust has for accomplishing message sending concurrency is the
*channel*, a programming concept that Rusts standard library provides an
implementation of. You can imagine a channel in programming like a channel of
@@ -557,21 +500,11 @@ discussing the use of patterns in `let` statements and destructuring in Chapter
18. Using a `let` statement in this way is a convenient way to extract the
pieces of the tuple returned by `mpsc::channel`.
<!-- above -- can you give us a general idea of what that means for us in this
program? -->
<!-- A general idea of what *what* means? I'm not sure what you're asking for;
I've added a bit of explanation of the destructuring but I'm not sure that's
what you meant /Carol -->
Lets move the transmitting end into a spawned thread and have it send one
string so that the spawned thread is communicating with the main thread, shown
in Listing 16-7. This is like putting a rubber duck in the river upstream or
sending a chat message from one thread to another:
<!-- Can you tell us why we want to do this, what does this do for us and our
program? -->
<!-- Elaborated /Carol -->
Filename: src/main.rs
```
@@ -634,11 +567,6 @@ sent, `recv` will return it in a `Result<T, E>`. When the sending end of the
channel closes, `recv` will return an error to signal that no more values will
be coming.
<!-- Why do we want it to error when the sending end closes? And what's the
advantage of blocking here? -->
<!-- We don't necessarily *want* it to error, that's just how the standard
library has implemented it. I've tried to clarify that and blocking. /Carol -->
The `try_recv` method doesnt block, but will instead return a `Result<T, E>`
immediately: an `Ok` value holding a message if one is available, and an `Err`
value if there arent any messages this time. Using `try_recv` is useful if
@@ -651,10 +579,6 @@ Weve chosen to use `recv` in this example for simplicity; we dont have any
other work for the main thread to do other than wait for messages, so blocking
the main thread is appropriate.
<!-- So what is the difference here, what are the different situations you
would want to return the value immediately? -->
<!-- Elaborated above /Carol -->
If we run the code in Listing 16-8, well see the value printed out from the
main thread:
@@ -666,18 +590,6 @@ Perfect!
### Channels and Ownership Transference
<!-- Hmm i'm not sure we need as it's own section, it seems like it could be
condensed now the reader is pretty familiar with ownership rules. We might not
even need the example, but I'll defer to you on this -->
<!-- We think the examples in this section are important-- there are likely
readers who are skeptical about Rust's ownership system and whether it's worth
putting up with, and this section is the payoff. We're trying to convince those
people that one big benefit Rust's ownership gives you is that it enables you
to write safe, concurrent code. I'm glad we've convinced *you*, but I'm not so
sure that we'll have convinced all our readers at this point! I've tried to
state this a bit more clearly without calling out these readers too
explicitly... /Carol -->
The ownership rules play a vital role in message sending as far as helping us
write safe, concurrent code. Preventing errors in concurrent programming is the
advantage we get by making the tradeoff of having to think about ownership
@@ -797,15 +709,6 @@ Because we dont have any code that pauses or delays in the `for` loop in the
main thread, we can tell that the main thread is waiting to receive values from
the spawned thread.
<!-- Above -- just to be clear, this is because the main thread is receiving
the pauses from the spawned thread, is that right? -->
<!-- It's not that we're sending the *pauses*, it's that the spawned thread is
pausing between sending each message, so then the main thread doesn't receive
all the messages all at the same time, it receives one message per second
because that's how they were sent. I'm not sure what in the code looks like
"sending/receiving the pauses" or what isn't clear here, so I'm not sure how to
fix it /Carol -->
### Creating Multiple Producers by Cloning the Transmitter
Near the start of this section, we mentioned that `mpsc` stood for *multiple
@@ -891,12 +794,6 @@ only one. Consider this slogan again:
What would “communicate by sharing memory” look like? And moreover, why would
message passing enthusiasts choose not to use it and do the opposite instead?
<!-- Can you expand here? I wasn't sure where we were getting the idea that
message passers hated and inverted memory sharing -->
<!-- I'm not sure where you got "hate" from :) I've tried to reword. We're
getting this idea from the slogan that the Go programming language espouses
that we discussed earlier /Carol -->
In a way, channels in any programming language are sort of like single
ownership, because once you transfer a value down a channel, you shouldnt use
that value any longer. Shared memory concurrency is sort of like multiple
@@ -922,10 +819,6 @@ data it holds via the locking system.
Mutexes have a reputation for being hard to use because there are some
rules you have to remember:
<!-- below -- what is the lock, here? Can you define that outright? And make it
clear that the mutex is the guard? -->
<!-- I've added definitions/explanations above /Carol -->
1. You must attempt to acquire the lock before using the data.
2. Once youre done with the data thats guarded by the mutex, you must unlock
the data so other threads can acquire the lock.
@@ -973,18 +866,10 @@ To access the data inside the mutex, we use the `lock` method to acquire the
lock. This call will block the current thread so that it cant do any work
until its our turn to have the lock.
<!-- will block what, other requests for the lock? Or block access to the data?
-->
<!-- This is where I hope our earlier definition of "block" that I added will
help; I've also reworded to reinforce that /Carol -->
The call to `lock` would fail if another thread holding the lock panicked. In
that case, no one would ever be able to get the lock, so weve chosen to
`unwrap` and have this thread panic if were in that situation.
<!-- As in, the lock would be released? What would failure look like? -->
<!-- As in we wouldn't ever be able to get the lock, I've clarified /Carol -->
Once weve acquired the lock, we can treat the return value, named `num` in
this case, as a mutable reference to the data inside. The type system ensures
that we acquire a lock before using this value: `Mutex<i32>` is not an `i32`,
@@ -1055,11 +940,6 @@ program.
We hinted that this example wont compile, now lets find out why!
<!-- Hm, since we already saw this error, where we need to include move, maybe
we could skip it here and just include move in the initial program, to focus
more on the new error and new concepts -- what do you think? -->
<!-- Ok, cut! /Carol -->
```
error[E0382]: capture of moved value: `counter`
-->
@@ -1220,11 +1100,6 @@ Send is not satisfied`. Were going to talk about `Send` in the next section;
its one of the traits that ensures the types we use with threads are meant for
use in concurrent situations.
<!-- Maybe we need to save this discussion until after talking about Send?
Otherwise, you might expand on this, what is the reader taking away here? -->
<!-- The reader should take away that we can't use `Rc<T>` with threads, and
we're not sure how to point that out without mentioning `Send`. /Carol -->
Unfortunately, `Rc<T>` is not safe to share across threads. When `Rc<T>`
manages the reference count, it adds to the count for each call to `clone` and
subtracts from the count when each clone is dropped, but it doesnt use any
@@ -1319,17 +1194,6 @@ in any language, and have a go at implementing them in Rust. The standard
library API documentation for `Mutex<T>` and `MutexGuard` will have useful
information.
<!--Rust's type system and ownership has made sure that our threads have
exclusive access to the shared value when they're updating it, so the threads
won't overwrite each other's answers in unpredictable ways. It took us a while
to work with the compiler to get everything right, but we've saved future time
that might be spent trying to reproduce subtly incorrect scenarios that only
happen when the threads run in a particular order.-->
<!-- Feel free to contradict me, but I think this has come across in the
chapters, I'm suggesting cutting just to keep focus, keep it moving -->
<!-- We're tentatively okay with cutting this, but again we want to convince
people who are skeptical that dealing with ownership is worth it /Carol -->
Lets round out this chapter by talking about the `Send` and `Sync` traits and
how we could use them with custom types.

View File

@@ -22,24 +22,12 @@ shipped to production. Weve nicknamed this aspect of Rust *fearless
concurrency*. Fearless concurrency allows you to write code thats free of
subtle bugs and is easy to refactor without introducing new bugs.
<!-- Can you say explicitly why making concurrency issues compile time errors
rather than runtime errors is an advantage? -->
<!-- I feel like we've explained this a few times now, but I suppose since the
advantage should be greater in concurrent code it's worth saying again /Carol
-->
> Note: well be referring to many of the problems here as *concurrent* rather
> than being more precise by saying *concurrent and/or parallel*, for
> simplicitys sake. If this were a book specifically about concurrency and/or
> parallelism, wed be sure to be more specific. For this chapter, please
> mentally substitute *concurrent and/or parallel* whenever we say *concurrent*.
<!-- I'm not sure what you mean about languages being strongly opinionated over
these issues and what kind of strategy that is, below, can you be more
specific? -->
<!-- I've added an example and elaborated on the strategy we're talking about
here. /Carol -->
Many languages are strongly opinionated about the solutions they offer for
dealing with concurrent problems. For example, Erlang has elegant functionality
for message passing concurrency, but only obscure ways to share state between

View File

@@ -5,10 +5,6 @@ In most operating systems today, an executed programs code is run in a
your program, you can also have independent parts that run simultaneously. The
feature that runs these independent parts is called *threads*.
<!-- I've tried to simplify the text above, can you check that I haven't
changed meaning? -->
<!-- Made some small tweaks, overall seems fine /Carol -->
Splitting the computation in your program up into multiple threads can improve
performance, since the program will be doing multiple things at the same time,
but it also adds complexity. Because threads may run simultaneously, theres no
@@ -22,10 +18,6 @@ threads will run. This can lead to problems such as:
- Bugs that only happen in certain situations and are hard to reproduce and
fix reliably
<!-- How do threads prevent each other from continuing? Or is that something
we'll cover later?-->
<!-- We don't really get into that later, so I've expanded a bit here /Carol -->
Rust attempts to mitigate negative effects of using threads. Programming in a
multithreaded context still takes careful thought and requires a code structure
thats different from programs that run in a single thread.
@@ -46,13 +38,6 @@ Each model has its own advantages and tradeoffs, and the tradeoff most
important to Rust is runtime support. *Runtime* is a confusing term and can
have different meanings in different contexts.
<!-- Below - you mean this is the cause of runtime? Or "runtime" literally
means the code included by Rust in every binary? -->
<!-- Runtime literally means the code included by Rust in every binary.
Wikipedia calls this "runtime system":
https://en.wikipedia.org/wiki/Runtime_system but most people colloquially just
say "the runtime". I've tried to clarify. /Carol -->
In this context, by runtime we mean code thats included by the language in
every binary. This code can be large or small depending on the language, but
every non-assembly language will have some amount of runtime code. For that
@@ -122,16 +107,12 @@ hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
```
<!-- This seems interesting, how come the threads often take turns, but not
always? -->
<!-- I've added a bit of clarification /Carol -->
The `thread::sleep` function will force a thread to stop its execution for a
The `thread::sleep` function will force a thread to stop its execution for a
short duration, allowing a different thread to run, so that they will probably
take turns, but thats not guaranteed: it depends on how your operating system
schedules the threads. In this run, the main thread printed first, even though
take turns, but thats not guaranteed: it depends on how your operating system
schedules the threads. In this run, the main thread printed first, even though
the print statement from the spawned thread appears first in the code. And even
though we told the spawned thread to print until `i` is 9, it only got to 5
though we told the spawned thread to print until `i` is 9, it only got to 5
before the main thread shut down.
If you run this code and only see one thread, or dont see any overlap, try
@@ -145,10 +126,6 @@ the time, because the main thread ends before the spawned thread is done,
theres actually no guarantee that the spawned thread will get to run at all,
because theres no guarantee on the order in which threads run!
<!-- Above -- why is this the case, because there are no guarantees over which
order the threads run in? -->
<!-- Yep! /Carol -->
We can fix this by saving the return value of `thread::spawn` in a variable.
The return type of `thread::spawn` is `JoinHandle`. A `JoinHandle` is an owned
value that, when we call the `join` method on it, will wait for its thread to
@@ -156,11 +133,6 @@ finish. Listing 16-2 shows how to use the `JoinHandle` of the thread we created
in Listing 16-1 and call `join` in order to make sure the spawned thread
finishes before the `main` exits:
<!-- Saving the return value where? I think this explanation of join handle
needs expanding, this feels cut short -->
<!-- In a variable. I've expanded a bit, but I'm not sure what information
seems missing, so I'm not sure if this is sufficient /Carol -->
<span class="filename">Filename: src/main.rs</span>
```rust
@@ -193,10 +165,6 @@ thread is prevented from performing work or exiting. Because weve put the cal
to `join` after the main threads `for` loop, running this example should
produce output that looks something like this:
<!-- Liz: I've added a definition of "block" in the context of threads here,
which is the first time we used the term-- it seemed to cause some confusion
later on. /Carol -->
```text
hi number 1 from the main thread!
hi number 2 from the main thread!
@@ -393,9 +361,6 @@ fn main() {
<span class="caption">Listing 16-5: Using the `move` keyword to force a closure
to take ownership of the values it uses</span>
<!-- Can you be more specific about the question we're asking about 16-4?-->
<!-- Done /Carol -->
What would happen to the code in Listing 16-4 where the main thread called
`drop` if we use a `move` closure? Would `move` fix that case? Nope, we get a
different error, because what Listing 16-4 is trying to do isnt allowed for a
@@ -427,10 +392,5 @@ rules when we try to use `v` in the main thread. The `move` keyword overrides
Rusts conservative default of borrowing; it doesnt let us violate the
ownership rules.
<!-- Uh oh, I'm lost again, I thought we were trying to fix 16-4 with move, but
we don't want it to work, is that right? Can you talk about this a little?-->
<!-- I've tried to clarify a bit in the paragraph before this error and a bit
after the error /Carol -->
Now that we have a basic understanding of threads and the thread API, lets
talk about what we can actually *do* with threads.

View File

@@ -10,9 +10,6 @@ documentation:
>
> --[Effective Go](http://golang.org/doc/effective_go.html)
<!-- below -- what is the channel, precisely? A crate? a technique?-->
<!-- I've elaborated /Carol -->
One major tool Rust has for accomplishing message sending concurrency is the
*channel*, a programming concept that Rusts standard library provides an
implementation of. You can imagine a channel in programming like a channel of
@@ -69,21 +66,11 @@ discussing the use of patterns in `let` statements and destructuring in
Chapter 18. Using a `let` statement in this way is a convenient way to extract
the pieces of the tuple returned by `mpsc::channel`.
<!-- above -- can you give us a general idea of what that means for us in this
program? -->
<!-- A general idea of what *what* means? I'm not sure what you're asking for;
I've added a bit of explanation of the destructuring but I'm not sure that's
what you meant /Carol -->
Lets move the transmitting end into a spawned thread and have it send one
string so that the spawned thread is communicating with the main thread, shown
in Listing 16-7. This is like putting a rubber duck in the river upstream or
sending a chat message from one thread to another:
<!-- Can you tell us why we want to do this, what does this do for us and our
program? -->
<!-- Elaborated /Carol -->
<span class="filename">Filename: src/main.rs</span>
```rust
@@ -148,11 +135,6 @@ sent, `recv` will return it in a `Result<T, E>`. When the sending end of the
channel closes, `recv` will return an error to signal that no more values will
be coming.
<!-- Why do we want it to error when the sending end closes? And what's the
advantage of blocking here? -->
<!-- We don't necessarily *want* it to error, that's just how the standard
library has implemented it. I've tried to clarify that and blocking. /Carol -->
The `try_recv` method doesnt block, but will instead return a `Result<T, E>`
immediately: an `Ok` value holding a message if one is available, and an `Err`
value if there arent any messages this time. Using `try_recv` is useful if
@@ -165,10 +147,6 @@ Weve chosen to use `recv` in this example for simplicity; we dont have any
other work for the main thread to do other than wait for messages, so blocking
the main thread is appropriate.
<!-- So what is the difference here, what are the different situations you
would want to return the value immediately? -->
<!-- Elaborated above /Carol -->
If we run the code in Listing 16-8, well see the value printed out from the
main thread:
@@ -180,18 +158,6 @@ Perfect!
### Channels and Ownership Transference
<!-- Hmm i'm not sure we need as it's own section, it seems like it could be
condensed now the reader is pretty familiar with ownership rules. We might not
even need the example, but I'll defer to you on this -->
<!-- We think the examples in this section are important-- there are likely
readers who are skeptical about Rust's ownership system and whether it's worth
putting up with, and this section is the payoff. We're trying to convince those
people that one big benefit Rust's ownership gives you is that it enables you
to write safe, concurrent code. I'm glad we've convinced *you*, but I'm not so
sure that we'll have convinced all our readers at this point! I've tried to
state this a bit more clearly without calling out these readers too
explicitly... /Carol -->
The ownership rules play a vital role in message sending as far as helping us
write safe, concurrent code. Preventing errors in concurrent programming is the
advantage we get by making the tradeoff of having to think about ownership
@@ -313,15 +279,6 @@ Because we dont have any code that pauses or delays in the `for` loop in the
main thread, we can tell that the main thread is waiting to receive values from
the spawned thread.
<!-- Above -- just to be clear, this is because the main thread is receiving
the pauses from the spawned thread, is that right? -->
<!-- It's not that we're sending the *pauses*, it's that the spawned thread is
pausing between sending each message, so then the main thread doesn't receive
all the messages all at the same time, it receives one message per second
because that's how they were sent. I'm not sure what in the code looks like
"sending/receiving the pauses" or what isn't clear here, so I'm not sure how to
fix it /Carol -->
### Creating Multiple Producers by Cloning the Transmitter
Near the start of this section, we mentioned that `mpsc` stood for *multiple

View File

@@ -9,12 +9,6 @@ only one. Consider this slogan again:
What would “communicate by sharing memory” look like? And moreover, why would
message passing enthusiasts choose not to use it and do the opposite instead?
<!-- Can you expand here? I wasn't sure where we were getting the idea that
message passers hated and inverted memory sharing -->
<!-- I'm not sure where you got "hate" from :) I've tried to reword. We're
getting this idea from the slogan that the Go programming language espouses
that we discussed earlier /Carol -->
In a way, channels in any programming language are sort of like single
ownership, because once you transfer a value down a channel, you shouldnt use
that value any longer. Shared memory concurrency is sort of like multiple
@@ -40,10 +34,6 @@ data it holds via the locking system.
Mutexes have a reputation for being hard to use because there are some
rules you have to remember:
<!-- below -- what is the lock, here? Can you define that outright? And make it
clear that the mutex is the guard? -->
<!-- I've added definitions/explanations above /Carol -->
1. You must attempt to acquire the lock before using the data.
2. Once youre done with the data thats guarded by the mutex, you must unlock
the data so other threads can acquire the lock.
@@ -91,18 +81,10 @@ To access the data inside the mutex, we use the `lock` method to acquire the
lock. This call will block the current thread so that it cant do any work
until its our turn to have the lock.
<!-- will block what, other requests for the lock? Or block access to the data?
-->
<!-- This is where I hope our earlier definition of "block" that I added will
help; I've also reworded to reinforce that /Carol -->
The call to `lock` would fail if another thread holding the lock panicked. In
that case, no one would ever be able to get the lock, so weve chosen to
`unwrap` and have this thread panic if were in that situation.
<!-- As in, the lock would be released? What would failure look like? -->
<!-- As in we wouldn't ever be able to get the lock, I've clarified /Carol -->
Once weve acquired the lock, we can treat the return value, named `num` in
this case, as a mutable reference to the data inside. The type system ensures
that we acquire a lock before using this value: `Mutex<i32>` is not an `i32`,
@@ -174,11 +156,6 @@ program.
We hinted that this example wont compile, now lets find out why!
<!-- Hm, since we already saw this error, where we need to include move, maybe
we could skip it here and just include move in the initial program, to focus
more on the new error and new concepts -- what do you think? -->
<!-- Ok, cut! /Carol -->
```text
error[E0382]: capture of moved value: `counter`
-->
@@ -339,11 +316,6 @@ Send is not satisfied`. Were going to talk about `Send` in the next section;
its one of the traits that ensures the types we use with threads are meant for
use in concurrent situations.
<!-- Maybe we need to save this discussion until after talking about Send?
Otherwise, you might expand on this, what is the reader taking away here? -->
<!-- The reader should take away that we can't use `Rc<T>` with threads, and
we're not sure how to point that out without mentioning `Send`. /Carol -->
Unfortunately, `Rc<T>` is not safe to share across threads. When `Rc<T>`
manages the reference count, it adds to the count for each call to `clone` and
subtracts from the count when each clone is dropped, but it doesnt use any
@@ -438,16 +410,5 @@ in any language, and have a go at implementing them in Rust. The standard
library API documentation for `Mutex<T>` and `MutexGuard` will have useful
information.
<!--Rust's type system and ownership has made sure that our threads have
exclusive access to the shared value when they're updating it, so the threads
won't overwrite each other's answers in unpredictable ways. It took us a while
to work with the compiler to get everything right, but we've saved future time
that might be spent trying to reproduce subtly incorrect scenarios that only
happen when the threads run in a particular order.-->
<!-- Feel free to contradict me, but I think this has come across in the
chapters, I'm suggesting cutting just to keep focus, keep it moving -->
<!-- We're tentatively okay with cutting this, but again we want to convince
people who are skeptical that dealing with ownership is worth it /Carol -->
Lets round out this chapter by talking about the `Send` and `Sync` traits and
how we could use them with custom types.