Move (pun intended) closure env capture/borrowing/etc to concurrency

This commit is contained in:
Carol (Nichols || Goulding) 2017-01-23 15:17:13 -05:00
parent 40dfd1eb11
commit 5fbd2fe758
3 changed files with 243 additions and 236 deletions

View File

@ -202,235 +202,10 @@ closure form instead
The compiler even reminds us that this only works with closures!
### Closures, Ownership, and Borrowing
The property of being allowed to use variables from the surrounding scope is
also subject to all of the usual rules around ownership and borrowing. Since
closures attempt to infer the types of their parameters, they also infer how
those parameters are borrowed. Closures make that inference by looking at how
they are used. Consider the example in Listing 13-5 that has functions that
borrow immutably, borrow mutably, and move their parameters, then closures that
reference values from their environment and call each of the functions. We'll
see how this affects inference of when a value is borrowed:
<figure>
<span class="filename">Filename: src/main.rs</span>
```rust
#[derive(Debug)]
struct Foo;
fn borrows(f: &Foo) {
println!("Took {:?} by reference.", f);
}
fn borrows_mut(f: &mut Foo) {
println!("Took {:?} by mutable reference.", f);
}
fn moves(f: Foo) {
println!("Took ownership of {:?}.", f);
}
fn main() {
let f1 = Foo;
let closure_that_borrows = |x| borrows(x);
closure_that_borrows(&f1);
let mut f2 = Foo;
let closure_that_borrows_mut = |y| borrows_mut(y);
closure_that_borrows_mut(&mut f2);
let f3 = Foo;
let closure_that_moves = |z| moves(z);
closure_that_moves(f3);
}
```
<figcaption>
Listing 13-5: Closures that borrow, borrow mutably, and take ownership of their
parameters, which is inferred from how the closure body uses the parameters
</figcaption>
</figure>
Here, Rust is able to look at how we use the parameters of each closure inside
their bodies. If the closure passes its parameter it to a function that takes
`&Foo`, then the type of the parameter must be `&Foo`. If it passes the
parameter to a function that takes `&mut Foo`, then the type of parameter must
be `&mut Foo`, and so on. If we try to use `f3` after the call to
`closure_that_moves` in the last line of `main`, we'll get a compiler error
since ownership of `f3` was transferred to `closure_that_moves`, which
transferred ownership to the function `moves`.
### Overriding Inferred Borrowing with the `move` Keyword
Rust will allow you to override the borrowing inference by using the `move`
keyword. This will cause all of the closure's parameters to be taken by
ownership, instead of whatever they were inferred as. Consider this example:
```rust
let mut num = 4;
{
let mut add_num = |x| num += x;
add_num(6);
}
assert_eq!(10, num);
```
In this case, the `add_num` closure took a mutable reference to `num`, then
when we called `add_num`, it mutated the underlying value. In the last line,
`num` contains 10, as we'd expect. We also needed to declare `add_num` itself
as `mut` too, because we're mutating its environment.
If we change the definition of `add_num` to a `move` closure, the behavior is
different:
```rust
let mut num = 4;
{
let mut add_num = move |x| num += x;
add_num(6);
}
assert_eq!(4, num);
```
In the last line, `num` now contains 4: `add_num` took ownership of a copy of
`num`, rather than mutably borrowing `num`.
One of the most common places you'll see the `move` keyword used is with
threads, since it's important that one thread is no longer allowed to use a
value once the value has been transferred to another thread through a closure
in order to prevent data races. We'll talk more about that in Chapter XX.
### Closures and Lifetimes
Remember Listing 10-8 from the Lifetime Syntax section of Chapter 10? It looked
like this:
```rust,ignore
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
```
This example doesn't compile since `x` doesn't have a long enough lifetime.
Because closures may borrow variables from their enclosing scope, we can
construct a similar example with a closure that borrows `x` and tries to return
that borrowed value. The code in Listing 13-6 also won't compile:
<figure>
```rust,ignore
{
let closure;
{
let x = 4;
closure = || x ; // A closure that takes no arguments and returns x.
}
}
```
<figcaption>
Listing 13-6: A closure that tries to return a borrowed value that does not live
long enough
</figcaption>
</figure>
We get an error because `x` does not live long enough:
```text
error: `x` does not live long enough
-->
|
8 | closure = || x ; // A closure that takes no arguments and returns x.
| -- ^ does not live long enough
| |
| capture occurs here
9 | }
| - borrowed value only lives until here
10 | }
| - borrowed value needs to live until here
```
To fix the error in the code in Listing 13-6, we can use the `move` keyword
from the last section to make the closure take ownership of `x`. Because `x` is
a number, it is a `Copy` type and therefore will be copied into the closure.
The code in Listing 13-7 will compile:
<figure>
```rust
{
let closure;
{
let mut x = 4;
closure = move || x ; // A closure that takes no arguments and returns x.
x = 5;
assert_eq!(closure(), 4);
}
}
```
<figcaption>
Listing 13-7: Moving a value into the closure to fix the lifetime error
</figcaption>
</figure>
Even though we modified `x` between the closure definition and `assert_eq!`,
since `closure` now has its own version, the changes to `x` won't change the
version of `x` that's in the closure.
Rust doesn't provide a way to say that some values a closure uses should be
borrowed and some should be moved; it's either all by inference or all moved by
adding the `move` keyword. However, we can accomplish the goal of borrowing
some values and taking ownership of others by combining `move` with some extra
bindings. Consider this example where we want to borrow `s1` but take ownership
of `s2`:
```rust
let s1 = String::from("hello");
let s2 = String::from("goodbye");
let r = &s1;
let calculation = move || {
r;
s2;
};
println!("Can still use s1 here but not s2: {}", s1);
```
We've declared `calculation` to `move` all the values it references. Before
defining `calculation`, we declare a new variable `r` that borrows `s1`. Then
in the body of the `calculation` closure, we use `r` instead of using `s1`
directly. The closure takes ownership of `r`, but `r` is a reference, so the
closure hasn't taken ownership of `s1` even though `calculation` uses `move`.
Creating closures that capture values from their environment is mostly used in
the context of starting new threads. We'll show some more examples and explain
more detail about this feature of closures in Chapter 16 when we talk about
concurrency.
### Closures as Function Parameters Using the `Fn` Traits

View File

@ -1,7 +1,7 @@
## Iterators
Iterators are a pattern in Rust that allows you to do some processing on a
sequence of items. For example, the code in Listing 13-8 adds one to each
sequence of items. For example, the code in Listing 13-5 adds one to each
number in a vector:
<figure>
@ -16,7 +16,7 @@ assert_eq!(v2, [2, 3, 4]);
<figcaption>
Listing 13-8: Using an iterator, `map`, and `collect` to add one to each number
Listing 13-5: Using an iterator, `map`, and `collect` to add one to each number
in a vector
</figcaption>
@ -53,7 +53,7 @@ behavior of an iterator adaptor like `map`.
In the previous section, you may have noticed a subtle difference in wording:
we said that `map` *adapts* an iterator, but `collect` *consumes* one. That was
intentional. By themselves, iterators won't do anything; they're lazy. That is,
if we write code like Listing 13-8 except we don't call `collect`:
if we write code like Listing 13-5 except we don't call `collect`:
```rust
let v1: Vec<i32> = vec![1, 2, 3];
@ -125,7 +125,7 @@ defining the body of the `next` method. The way we want our iterator to work
is to add one to the state (which is why we initialized `count` to 0, since we
want our iterator to return one first). If `count` is still less than six, we'll
return the current value, but if `count` is six or higher, our iterator will
return `None`:
return `None`, as shown in Listing 13-6:
<figure>
@ -154,7 +154,7 @@ impl Iterator for Counter {
<figcaption>
Listing 13-9: Implementing the `Iterator` trait on our `Counter` struct
Listing 13-6: Implementing the `Iterator` trait on our `Counter` struct
</figcaption>
</figure>
@ -218,8 +218,8 @@ line.
### All Sorts of `Iterator` Adaptors
In Listing 13-8, we had iterators and we called methods like `map` and
`collect` on them. In Listing 13-9, however, we only implemented the `next`
In Listing 13-5, we had iterators and we called methods like `map` and
`collect` on them. In Listing 13-6, however, we only implemented the `next`
method on our `Counter`. How do we get methods like `map` and `collect` on our
`Counter`?

View File

@ -41,6 +41,238 @@ Code examples - just print stuff, no data sharing
## Communicating between threads
### Closures, Ownership, and Borrowing
The property of being allowed to use variables from the surrounding scope is
also subject to all of the usual rules around ownership and borrowing. Since
closures attempt to infer the types of their parameters, they also infer how
those parameters are borrowed. Closures make that inference by looking at how
they are used. Consider the example in Listing 13-5 that has functions that
borrow immutably, borrow mutably, and move their parameters, then closures that
reference values from their environment and call each of the functions. We'll
see how this affects inference of when a value is borrowed:
<figure>
<span class="filename">Filename: src/main.rs</span>
```rust
#[derive(Debug)]
struct Foo;
fn borrows(f: &Foo) {
println!("Took {:?} by reference.", f);
}
fn borrows_mut(f: &mut Foo) {
println!("Took {:?} by mutable reference.", f);
}
fn moves(f: Foo) {
println!("Took ownership of {:?}.", f);
}
fn main() {
let f1 = Foo;
let closure_that_borrows = |x| borrows(x);
closure_that_borrows(&f1);
let mut f2 = Foo;
let closure_that_borrows_mut = |y| borrows_mut(y);
closure_that_borrows_mut(&mut f2);
let f3 = Foo;
let closure_that_moves = |z| moves(z);
closure_that_moves(f3);
}
```
<figcaption>
Listing 16-something: Closures that borrow, borrow mutably, and take ownership
of their parameters, which is inferred from how the closure body uses the
parameters
</figcaption>
</figure>
Here, Rust is able to look at how we use the parameters of each closure inside
their bodies. If the closure passes its parameter it to a function that takes
`&Foo`, then the type of the parameter must be `&Foo`. If it passes the
parameter to a function that takes `&mut Foo`, then the type of parameter must
be `&mut Foo`, and so on. If we try to use `f3` after the call to
`closure_that_moves` in the last line of `main`, we'll get a compiler error
since ownership of `f3` was transferred to `closure_that_moves`, which
transferred ownership to the function `moves`.
### Overriding Inferred Borrowing with the `move` Keyword
Rust will allow you to override the borrowing inference by using the `move`
keyword. This will cause all of the closure's parameters to be taken by
ownership, instead of whatever they were inferred as. Consider this example:
```rust
let mut num = 4;
{
let mut add_num = |x| num += x;
add_num(6);
}
assert_eq!(10, num);
```
In this case, the `add_num` closure took a mutable reference to `num`, then
when we called `add_num`, it mutated the underlying value. In the last line,
`num` contains 10, as we'd expect. We also needed to declare `add_num` itself
as `mut` too, because we're mutating its environment.
If we change the definition of `add_num` to a `move` closure, the behavior is
different:
```rust
let mut num = 4;
{
let mut add_num = move |x| num += x;
add_num(6);
}
assert_eq!(4, num);
```
In the last line, `num` now contains 4: `add_num` took ownership of a copy of
`num`, rather than mutably borrowing `num`.
One of the most common places you'll see the `move` keyword used is with
threads, since it's important that one thread is no longer allowed to use a
value once the value has been transferred to another thread through a closure
in order to prevent data races. We'll talk more about that in Chapter XX.
### Closures and Lifetimes
Remember Listing 10-8 from the Lifetime Syntax section of Chapter 10? It looked
like this:
```rust,ignore
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
```
This example doesn't compile since `x` doesn't have a long enough lifetime.
Because closures may borrow variables from their enclosing scope, we can
construct a similar example with a closure that borrows `x` and tries to return
that borrowed value. The code in Listing 13-6 also won't compile:
<figure>
```rust,ignore
{
let closure;
{
let x = 4;
closure = || x ; // A closure that takes no arguments and returns x.
}
}
```
<figcaption>
Listing 16-something: A closure that tries to return a borrowed value that does
not live long enough
</figcaption>
</figure>
We get an error because `x` does not live long enough:
```text
error: `x` does not live long enough
-->
|
8 | closure = || x ; // A closure that takes no arguments and returns x.
| -- ^ does not live long enough
| |
| capture occurs here
9 | }
| - borrowed value only lives until here
10 | }
| - borrowed value needs to live until here
```
To fix the error in the code in Listing 13-6, we can use the `move` keyword
from the last section to make the closure take ownership of `x`. Because `x` is
a number, it is a `Copy` type and therefore will be copied into the closure.
The code in Listing 13-7 will compile:
<figure>
```rust
{
let closure;
{
let mut x = 4;
closure = move || x ; // A closure that takes no arguments and returns x.
x = 5;
assert_eq!(closure(), 4);
}
}
```
<figcaption>
Listing 16-something: Moving a value into the closure to fix the lifetime error
</figcaption>
</figure>
Even though we modified `x` between the closure definition and `assert_eq!`,
since `closure` now has its own version, the changes to `x` won't change the
version of `x` that's in the closure.
Rust doesn't provide a way to say that some values a closure uses should be
borrowed and some should be moved; it's either all by inference or all moved by
adding the `move` keyword. However, we can accomplish the goal of borrowing
some values and taking ownership of others by combining `move` with some extra
bindings. Consider this example where we want to borrow `s1` but take ownership
of `s2`:
```rust
let s1 = String::from("hello");
let s2 = String::from("goodbye");
let r = &s1;
let calculation = move || {
r;
s2;
};
println!("Can still use s1 here but not s2: {}", s1);
```
We've declared `calculation` to `move` all the values it references. Before
defining `calculation`, we declare a new variable `r` that borrows `s1`. Then
in the body of the `calculation` closure, we use `r` instead of using `s1`
directly. The closure takes ownership of `r`, but `r` is a reference, so the
closure hasn't taken ownership of `s1` even though `calculation` uses `move`.
### `Channels`
Look up examples of cases where channels are useful