mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-01-23 15:40:27 +08:00
Move (pun intended) closure env capture/borrowing/etc to concurrency
This commit is contained in:
parent
40dfd1eb11
commit
5fbd2fe758
@ -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
|
||||
|
||||
|
@ -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`?
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user