mirror of https://github.com/rust-lang/book
Propagate edits to ch13 to src
This commit is contained in:
parent
b24cc4d6b7
commit
daa268a0cd
|
@ -75,7 +75,7 @@
|
|||
## Thinking in Rust
|
||||
|
||||
- [Functional Language Features: Iterators and Closures](ch13-00-functional-features.md)
|
||||
- [Closures: Anonymous Functions that Can Capture Their Environment](ch13-01-closures.md)
|
||||
- [Closures: Anonymous Functions that Capture Their Environment](ch13-01-closures.md)
|
||||
- [Processing a Series of Items with Iterators](ch13-02-iterators.md)
|
||||
- [Improving Our I/O Project](ch13-03-improving-our-io-project.md)
|
||||
- [Comparing Performance: Loops vs. Iterators](ch13-04-performance.md)
|
||||
|
|
|
@ -14,11 +14,11 @@ More specifically, we’ll cover:
|
|||
|
||||
* *Closures*, a function-like construct you can store in a variable
|
||||
* *Iterators*, a way of processing a series of elements
|
||||
* How to use these two features to improve the I/O project in Chapter 12
|
||||
* The performance of these two features (Spoiler alert: they’re faster than you
|
||||
might think!)
|
||||
* How to use closures and iterators to improve the I/O project in Chapter 12
|
||||
* The performance of closures and iterators (Spoiler alert: they’re faster than
|
||||
you might think!)
|
||||
|
||||
Other Rust features, such as pattern matching and enums, which we’ve covered in
|
||||
other chapters, are influenced by the functional style as well. Mastering
|
||||
We’ve already covered some other Rust features, such as pattern matching and
|
||||
enums, that are also influenced by the functional style. Because mastering
|
||||
closures and iterators is an important part of writing idiomatic, fast Rust
|
||||
code, so we’ll devote this entire chapter to them.
|
||||
code, we’ll devote this entire chapter to them.
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
## Closures: Anonymous Functions that Can Capture Their Environment
|
||||
<!-- Old heading. Do not remove or links may break. -->
|
||||
<a id="closures-anonymous-functions-that-can-capture-their-environment"></a>
|
||||
|
||||
## Closures: Anonymous Functions that Capture Their Environment
|
||||
|
||||
Rust’s closures are anonymous functions you can save in a variable or pass as
|
||||
arguments to other functions. You can create the closure in one place and then
|
||||
call the closure to evaluate it in a different context. Unlike functions,
|
||||
closures can capture values from the scope in which they’re defined. We’ll
|
||||
demonstrate how these closure features allow for code reuse and behavior
|
||||
call the closure elsewhere to evaluate it in a different context. Unlike
|
||||
functions, closures can capture values from the scope in which they’re defined.
|
||||
We’ll demonstrate how these closure features allow for code reuse and behavior
|
||||
customization.
|
||||
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
|
@ -14,22 +17,23 @@ customization.
|
|||
|
||||
### Capturing the Environment with Closures
|
||||
|
||||
The first aspect of closures we’re going to examine is that closures can
|
||||
capture values from the environment they’re defined in for later use. Here’s
|
||||
the scenario: A t-shirt company gives away a free shirt to someone on their
|
||||
mailing list every so often. People on the mailing list can optionally add
|
||||
their favorite color to their profile. If the person chosen to get the free
|
||||
shirt has their favorite color in their profile, they get that color shirt. If
|
||||
the person hasn’t specified a favorite color, they get the color that the
|
||||
company currently has the most of.
|
||||
We’ll first examine how we can use closures to capture values from the
|
||||
environment they’re defined in for later use. Here’s the scenario: Every so
|
||||
often, our t-shirt company gives away an exclusive, limited-edition shirt to
|
||||
someone on our mailing list as a promotion. People on the mailing list can
|
||||
optionally add their favorite color to their profile. If the person chosen for
|
||||
a free shirt has their favorite color set, they get that color shirt. If the
|
||||
person hasn’t specified a favorite color, they get whatever color the company
|
||||
currently has the most of.
|
||||
|
||||
There are many ways to implement this. For this example, we’re going to use an
|
||||
enum called `ShirtColor` that has the variants `Red` and `Blue`. The
|
||||
company’s inventory is represented by an `Inventory` struct that has a field
|
||||
named `shirts` that contains a `Vec<ShirtColor>` representing the shirts
|
||||
currently in stock. The method `shirt_giveaway` defined on `Inventory` gets the
|
||||
optional shirt color preference of the person getting the free shirt, and
|
||||
returns the shirt color the person will get. This is shown in Listing 13-1:
|
||||
enum called `ShirtColor` that has the variants `Red` and `Blue` (limiting the
|
||||
number of colors available for simplicity). We represent the company’s
|
||||
inventory with an `Inventory` struct that has a field named `shirts` that
|
||||
contains a `Vec<ShirtColor>` representing the shirt colors currently in stock.
|
||||
The method `shirt_giveaway` defined on `Inventory` gets the optional shirt
|
||||
color preference of the free shirt winner, and returns the shirt color the
|
||||
person will get. This setup is shown in Listing 13-1:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -37,46 +41,56 @@ returns the shirt color the person will get. This is shown in Listing 13-1:
|
|||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-01/src/main.rs}}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 13-1: Shirt company giveaway</span>
|
||||
<span class="caption">Listing 13-1: Shirt company giveaway situation</span>
|
||||
|
||||
The `store` defined in `main` has two blue shirts and one red shirt in stock.
|
||||
Then it calls the `giveaway` method for a user with a preference for a red
|
||||
shirt and a user without any preference. Running this code prints:
|
||||
The `store` defined in `main` has two blue shirts and one red shirt remaining
|
||||
to distribute for this limited-edition promotion. We call the `giveaway` method
|
||||
for a user with a preference for a red shirt and a user without any preference.
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch13-functional-features/listing-13-01/output.txt}}
|
||||
```
|
||||
|
||||
Again, this code could be implemented in many ways, but this way uses concepts
|
||||
you’ve already learned, except for the body of the `giveaway` method that uses
|
||||
a closure. The `giveaway` method takes the user preference `Option<ShirtColor>`
|
||||
and calls `unwrap_or_else` on it. The [`unwrap_or_else` method on
|
||||
Again, this code could be implemented in many ways, and here, to focus on
|
||||
closures, we’ve stuck to concepts you’ve already learned except for the body of
|
||||
the `giveaway` method that uses a closure. In the `giveaway` method, we get the
|
||||
user preference as a parameter of type `Option<ShirtColor>` and call the
|
||||
`unwrap_or_else` method on `user_preference`. The [`unwrap_or_else` method on
|
||||
`Option<T>`][unwrap-or-else]<!-- ignore --> is defined by the standard library.
|
||||
It takes one argument: a closure without any arguments that returns a value `T`
|
||||
(the same type stored in the `Some` variant of the `Option<T>`, in this case, a
|
||||
(the same type stored in the `Some` variant of the `Option<T>`, in this case
|
||||
`ShirtColor`). If the `Option<T>` is the `Some` variant, `unwrap_or_else`
|
||||
returns the value from within the `Some`. If the `Option<T>` is the `None`
|
||||
variant, `unwrap_or_else` calls the closure and returns the value returned by
|
||||
the closure.
|
||||
|
||||
This is interesting because we’ve passed a closure that calls
|
||||
We specify the closure expression `|| self.most_stocked()` as the argument to
|
||||
`unwrap_or_else`. This is a closure that takes no parameters itself (if the
|
||||
closure had parameters, they would appear between the two vertical bars). The
|
||||
body of the closure calls `self.most_stocked()`. We’re defining the closure
|
||||
here, and the implementation of `unwrap_or_else` will evaluate the closure
|
||||
later if the result is needed.
|
||||
|
||||
Running this code prints:
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch13-functional-features/listing-13-01/output.txt}}
|
||||
```
|
||||
|
||||
One interesting aspect here is that we’ve passed a closure that calls
|
||||
`self.most_stocked()` on the current `Inventory` instance. The standard library
|
||||
didn’t need to know anything about the `Inventory` or `ShirtColor` types we
|
||||
defined, or the logic we want to use in this scenario. The closure captured an
|
||||
immutable reference to the `self` `Inventory` instance and passed it with the
|
||||
code we specified to the `unwrap_or_else` method. Functions are not able to
|
||||
capture their environment in this way.
|
||||
defined, or the logic we want to use in this scenario. The closure captures an
|
||||
immutable reference to the `self` `Inventory` instance and passes it with the
|
||||
code we specify to the `unwrap_or_else` method. Functions, on the other hand,
|
||||
are not able to capture their environment in this way.
|
||||
|
||||
### Closure Type Inference and Annotation
|
||||
|
||||
There are more differences between functions and closures. Closures don’t
|
||||
usually require you to annotate the types of the parameters or the return value
|
||||
like `fn` functions do. Type annotations are required on functions because
|
||||
they’re part of an explicit interface exposed to your users. Defining this
|
||||
like `fn` functions do. Type annotations are required on functions because the
|
||||
types are part of an explicit interface exposed to your users. Defining this
|
||||
interface rigidly is important for ensuring that everyone agrees on what types
|
||||
of values a function uses and returns. But closures aren’t used in an exposed
|
||||
interface like this: they’re stored in variables and used without naming them
|
||||
and exposing them to users of our library.
|
||||
of values a function uses and returns. Closures, on the other hand, aren’t used
|
||||
in an exposed interface like this: they’re stored in variables and used without
|
||||
naming them and exposing them to users of our library.
|
||||
|
||||
Closures are typically short and relevant only within a narrow context rather
|
||||
than in any arbitrary scenario. Within these limited contexts, the compiler can
|
||||
|
@ -87,7 +101,9 @@ needs closure type annotations too).
|
|||
As with variables, we can add type annotations if we want to increase
|
||||
explicitness and clarity at the cost of being more verbose than is strictly
|
||||
necessary. Annotating the types for a closure would look like the definition
|
||||
shown in Listing 13-2.
|
||||
shown in Listing 13-2. In this example, we’re defining a closure and storing it
|
||||
in a variable rather than defining the closure in the spot we pass it as an
|
||||
argument as we did in Listing 13-1.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -99,11 +115,11 @@ shown in Listing 13-2.
|
|||
parameter and return value types in the closure</span>
|
||||
|
||||
With type annotations added, the syntax of closures looks more similar to the
|
||||
syntax of functions. The following is a vertical comparison of the syntax for
|
||||
the definition of a function that adds 1 to its parameter and a closure that
|
||||
has the same behavior. We’ve added some spaces to line up the relevant parts.
|
||||
This illustrates how closure syntax is similar to function syntax except for
|
||||
the use of pipes and the amount of syntax that is optional:
|
||||
syntax of functions. Here we define a function that adds 1 to its parameter and
|
||||
a closure that has the same behavior, for comparison. We’ve added some spaces
|
||||
to line up the relevant parts. This illustrates how closure syntax is similar
|
||||
to function syntax except for the use of pipes and the amount of syntax that is
|
||||
optional:
|
||||
|
||||
```rust,ignore
|
||||
fn add_one_v1 (x: u32) -> u32 { x + 1 }
|
||||
|
@ -113,20 +129,23 @@ let add_one_v4 = |x| x + 1 ;
|
|||
```
|
||||
|
||||
The first line shows a function definition, and the second line shows a fully
|
||||
annotated closure definition. The third line removes the type annotations from
|
||||
the closure definition, and the fourth line removes the brackets, which are
|
||||
optional because the closure body has only one expression. These are all valid
|
||||
definitions that will produce the same behavior when they’re called. Calling
|
||||
the closures is required for `add_one_v3` and `add_one_v4` to be able to
|
||||
compile because the types will be inferred from their usage.
|
||||
annotated closure definition. In the third line, we remove the type annotations
|
||||
from the closure definition. In the fourth line, we remove the brackets, which
|
||||
are optional because the closure body has only one expression. These are all
|
||||
valid definitions that will produce the same behavior when they’re called.
|
||||
Evaluating the closures is required for `add_one_v3` and `add_one_v4` to be
|
||||
able to compile because the types will be inferred from their usage. This is
|
||||
similar to `let v = Vec::new();` needing either type annotations or values of
|
||||
some type to be inserted into the `Vec` for Rust to be able to infer the type.
|
||||
|
||||
Closure definitions will have one concrete type inferred for each of their
|
||||
parameters and for their return value. For instance, Listing 13-3 shows the
|
||||
definition of a short closure that just returns the value it receives as a
|
||||
For closure definitions, the compiler will infer one concrete type for each of
|
||||
their parameters and for their return value. For instance, Listing 13-3 shows
|
||||
the definition of a short closure that just returns the value it receives as a
|
||||
parameter. This closure isn’t very useful except for the purposes of this
|
||||
example. Note that we haven’t added any type annotations to the definition: if
|
||||
we then try to call the closure twice, using a `String` as an argument the
|
||||
first time and a `u32` the second time, we’ll get an error.
|
||||
example. Note that we haven’t added any type annotations to the definition.
|
||||
Because there are no type annotations, we can call the closure with any type,
|
||||
which we’ve done here with `String` the first time. If we then try to call
|
||||
`example_closure` with an integer, we’ll get an error.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -146,7 +165,7 @@ The compiler gives us this error:
|
|||
The first time we call `example_closure` with the `String` value, the compiler
|
||||
infers the type of `x` and the return type of the closure to be `String`. Those
|
||||
types are then locked into the closure in `example_closure`, and we get a type
|
||||
error if we try to use a different type with the same closure.
|
||||
error when we next try to use a different type with the same closure.
|
||||
|
||||
### Capturing References or Moving Ownership
|
||||
|
||||
|
@ -156,11 +175,9 @@ immutably, borrowing mutably, and taking ownership. The closure will decide
|
|||
which of these to use based on what the body of the function does with the
|
||||
captured values.
|
||||
|
||||
Listing 13-4 defines a closure that captures an immutable borrow to the vector
|
||||
named `list` because it only needs an immutable borrow to print the value. This
|
||||
example also illustrates that a variable can bind to a closure definition, and
|
||||
the closure can later be called by using the variable name and parentheses as
|
||||
if the variable name were a function name:
|
||||
In Listing 13-4, we define a closure that captures an immutable reference to
|
||||
the vector named `list` because it only needs an immutable reference to print
|
||||
the value:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -169,19 +186,23 @@ if the variable name were a function name:
|
|||
```
|
||||
|
||||
<span class="caption">Listing 13-4: Defining and calling a closure that
|
||||
captures an immutable borrow</span>
|
||||
captures an immutable reference</span>
|
||||
|
||||
The `list` is still accessible by the code before the closure definition, after
|
||||
This example also illustrates that a variable can bind to a closure definition,
|
||||
and we can later call the closure by using the variable name and parentheses as
|
||||
if the variable name were a function name.
|
||||
|
||||
Because we can have multiple immutable references to `list` at the same time,
|
||||
`list` is still accessible from the code before the closure definition, after
|
||||
the closure definition but before the closure is called, and after the closure
|
||||
is called because we can have multiple immutable borrows of `list` at the same
|
||||
time. This code compiles, runs, and prints:
|
||||
is called. This code compiles, runs, and prints:
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch13-functional-features/listing-13-04/output.txt}}
|
||||
```
|
||||
|
||||
Next, Listing 13-5 changes the closure definition to need a mutable borrow
|
||||
because the closure body adds an element to the `list` vector:
|
||||
Next, in Listing 13-5, we change the closure body so that it adds an element to
|
||||
the `list` vector. The closure now captures a mutable reference:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -190,7 +211,7 @@ because the closure body adds an element to the `list` vector:
|
|||
```
|
||||
|
||||
<span class="caption">Listing 13-5: Defining and calling a closure that
|
||||
captures a mutable borrow</span>
|
||||
captures a mutable reference</span>
|
||||
|
||||
This code compiles, runs, and prints:
|
||||
|
||||
|
@ -200,49 +221,52 @@ This code compiles, runs, and prints:
|
|||
|
||||
Note that there’s no longer a `println!` between the definition and the call of
|
||||
the `borrows_mutably` closure: when `borrows_mutably` is defined, it captures a
|
||||
mutable reference to `list`. After the closure is called, because we don’t use
|
||||
the closure again after that point, the mutable borrow ends. Between the
|
||||
closure definition and the closure call, an immutable borrow to print isn’t
|
||||
allowed because no other borrows are allowed when there’s a mutable borrow. Try
|
||||
adding a `println!` there to see what error message you get!
|
||||
mutable reference to `list`. We don’t use the closure again after the closure
|
||||
is called, so the mutable borrow ends. Between the closure definition and the
|
||||
closure call, an immutable borrow to print isn’t allowed because no other
|
||||
borrows are allowed when there’s a mutable borrow. Try adding a `println!`
|
||||
there to see what error message you get!
|
||||
|
||||
If you want to force the closure to take ownership of the values it uses in the
|
||||
environment even though the body of the closure doesn’t strictly need
|
||||
ownership, you can use the `move` keyword before the parameter list. This
|
||||
technique is mostly useful when passing a closure to a new thread to move the
|
||||
data so it’s owned by the new thread. We’ll have more examples of `move`
|
||||
data so that it’s owned by the new thread. We’ll have more examples of `move`
|
||||
closures in Chapter 16 when we talk about concurrency.
|
||||
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<a id="storing-closures-using-generic-parameters-and-the-fn-traits"></a>
|
||||
<a id="limitations-of-the-cacher-implementation"></a>
|
||||
<a id="moving-captured-values-out-of-the-closure-and-the-fn-traits"></a>
|
||||
|
||||
### Moving Captured Values Out of the Closure and the `Fn` Traits
|
||||
### Moving Captured Values Out of Closures and the `Fn` Traits
|
||||
|
||||
Once a closure has captured a reference or moved a value into the closure, the
|
||||
code in the body of the function also affects what happens to the references or
|
||||
values as a result of calling the function. A closure body can move a captured
|
||||
value out of the closure, can mutate the captured value, can neither move nor
|
||||
mutate the captured value, or can capture nothing from the environment. The way
|
||||
a closure captures and handles values from the environment affects which traits
|
||||
the closure implements. The traits are how functions and structs can specify
|
||||
what kinds of closures they can use.
|
||||
Once a closure has captured a reference or captured ownership of a value where
|
||||
the closure is defined (thus affecting what, if anything, is moved *into* the
|
||||
closure), the code in the body of the closure defines what happens to the
|
||||
references or values when the closure is evaluated later (thus affecting what,
|
||||
if anything, is moved *out of* the closure). A closure body can do any of the
|
||||
following: move a captured value out of the closure, mutate the captured value,
|
||||
neither move nor mutate the value, or capture nothing from the environment to
|
||||
begin with.
|
||||
|
||||
Closures will automatically implement one, two, or all three of these `Fn`
|
||||
traits, in an additive fashion:
|
||||
The way a closure captures and handles values from the environment affects
|
||||
which traits the closure implements, and traits are how functions and structs
|
||||
can specify what kinds of closures they can use. Closures will automatically
|
||||
implement one, two, or all three of these `Fn` traits, in an additive fashion:
|
||||
|
||||
1. `FnOnce` applies to closures that can be called at least once. All closures
|
||||
implement this trait, because all closures can be called. If a closure moves
|
||||
captured values out of its body, then that closure only implements `FnOnce`
|
||||
and not any of the other `Fn` traits, because it can only be called once.
|
||||
implement at least this trait, because all closures can be called. A closure
|
||||
that moves captured values out of its body will only implement `FnOnce`
|
||||
and none of the other `Fn` traits, because it can only be called once.
|
||||
2. `FnMut` applies to closures that don’t move captured values out of their
|
||||
body, but that might mutate the captured values. These closures can be
|
||||
called more than once.
|
||||
3. `Fn` applies to closures that don’t move captured values out of their body
|
||||
and that don’t mutate captured values. These closures can be called more
|
||||
than once without mutating their environment, which is important in cases
|
||||
such as calling a closure multiple times concurrently. Closures that don’t
|
||||
capture anything from their environment implement `Fn`.
|
||||
and that don’t mutate captured values, as well as closures that capture
|
||||
nothing from their environment. These closures can be called more than once
|
||||
without mutating their environment, which is important in cases such as
|
||||
calling a closure multiple times concurrently.
|
||||
|
||||
Let’s look at the definition of the `unwrap_or_else` method on `Option<T>` that
|
||||
we used in Listing 13-6:
|
||||
|
@ -266,8 +290,8 @@ Recall that `T` is the generic type representing the type of the value in the
|
|||
`unwrap_or_else` function: code that calls `unwrap_or_else` on an
|
||||
`Option<String>`, for example, will get a `String`.
|
||||
|
||||
Next, notice that the `unwrap_or_else` function has an additional generic type
|
||||
parameter, `F`. The `F` type is the type of the parameter named `f`, which is
|
||||
Next, notice that the `unwrap_or_else` function has the additional generic type
|
||||
parameter `F`. The `F` type is the type of the parameter named `f`, which is
|
||||
the closure we provide when calling `unwrap_or_else`.
|
||||
|
||||
The trait bound specified on the generic type `F` is `FnOnce() -> T`, which
|
||||
|
@ -287,11 +311,13 @@ of closures and is as flexible as it can be.
|
|||
> value is `None`.
|
||||
|
||||
Now let’s look at the standard library method `sort_by_key` defined on slices,
|
||||
to see how that differs. It takes a closure that implements `FnMut`. The
|
||||
closure gets one argument, a reference to the current item in the slice being
|
||||
considered, and returns a value of type `K` that can be ordered. This function
|
||||
is useful when you want to sort a slice by a particular attribute of each item.
|
||||
In Listing 13-7, we have a list of `Rectangle` instances and we use
|
||||
to see how that differs from `unwrap_or_else` and why `sort_by_key` uses
|
||||
`FnMut` instead of `FnOnce` for the trait bound.
|
||||
|
||||
The closure gets one argument, a reference to the current item in the slice
|
||||
being considered, and returns a value of type `K` that can be ordered. This
|
||||
function is useful when you want to sort a slice by a particular attribute of
|
||||
each item. In Listing 13-x, we have a list of `Rectangle` instances and we use
|
||||
`sort_by_key` to order them by their `width` attribute from low to high:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
@ -300,8 +326,8 @@ In Listing 13-7, we have a list of `Rectangle` instances and we use
|
|||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-07/src/main.rs}}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 13-7: Using `sort_by_key` and a closure to sort a
|
||||
list of `Rectangle` instances by their `width` value</span>
|
||||
<span class="caption">Listing 13-7: Using `sort_by_key` to order rectangles by
|
||||
width</span>
|
||||
|
||||
This code prints:
|
||||
|
||||
|
@ -314,9 +340,9 @@ the closure multiple times: once for each item in the slice. The closure `|r|
|
|||
r.width` doesn’t capture, mutate, or move out anything from its environment, so
|
||||
it meets the trait bound requirements.
|
||||
|
||||
In contrast, Listing 13-8 shows an example of a closure that only implements
|
||||
`FnOnce` because it moves a value out of the environment. The compiler won’t
|
||||
let us use this closure with `sort_by_key`:
|
||||
In contrast, Listing 13-8 shows an example of a closure that implements just
|
||||
the `FnOnce` trait, because it moves a value out of the environment. The
|
||||
compiler won’t let us use this closure with `sort_by_key`:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -329,8 +355,8 @@ let us use this closure with `sort_by_key`:
|
|||
|
||||
This is a contrived, convoluted way (that doesn’t work) to try and count the
|
||||
number of times `sort_by_key` gets called when sorting `list`. This code
|
||||
attempts to do this counting by pushing `value`, a `String` from the closure’s
|
||||
environment, into the `sort_operations` vector. The closure captures `value`
|
||||
attempts to do this counting by pushing `value`—a `String` from the closure’s
|
||||
environment—into the `sort_operations` vector. The closure captures `value`
|
||||
then moves `value` out of the closure by transferring ownership of `value` to
|
||||
the `sort_operations` vector. This closure can be called once; trying to call
|
||||
it a second time wouldn’t work because `value` would no longer be in the
|
||||
|
@ -345,12 +371,12 @@ implement `FnMut`:
|
|||
|
||||
The error points to the line in the closure body that moves `value` out of the
|
||||
environment. To fix this, we need to change the closure body so that it doesn’t
|
||||
move values out of the environment. If we’re interested in the number of times
|
||||
`sort_by_key` is called, keeping a counter in the environment and incrementing
|
||||
its value in the closure body is a more straightforward way to calculate that.
|
||||
The closure in Listing 13-9 works with `sort_by_key` because it is only
|
||||
capturing a mutable reference to the `num_sort_operations` counter and can
|
||||
therefore be called more than once:
|
||||
move values out of the environment. To count the number of times `sort_by_key`
|
||||
is called, keeping a counter in the environment and incrementing its value in
|
||||
the closure body is a more straightforward way to calculate that. The closure
|
||||
in Listing 13-x works with `sort_by_key` because it is only capturing a mutable
|
||||
reference to the `num_sort_operations` counter and can therefore be called more
|
||||
than once:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -362,8 +388,8 @@ therefore be called more than once:
|
|||
is allowed</span>
|
||||
|
||||
The `Fn` traits are important when defining or using functions or types that
|
||||
make use of closures. The next section discusses iterators, and many iterator
|
||||
methods take closure arguments. Keep these details of closures in mind as we
|
||||
explore iterators!
|
||||
make use of closures. In the next section, we’ll discuss iterators. Many
|
||||
iterator methods take closure arguments, so keep these closure details in mind
|
||||
as we continue!
|
||||
|
||||
[unwrap-or-else]: ../std/option/enum.Option.html#method.unwrap_or_else
|
||||
|
|
|
@ -17,16 +17,16 @@ useful.
|
|||
|
||||
<span class="caption">Listing 13-10: Creating an iterator</span>
|
||||
|
||||
Once we’ve created an iterator, we can use it in a variety of ways. In Listing
|
||||
3-5 in Chapter 3, we iterated over an array using a `for` loop to execute some
|
||||
code on each of its items. Under the hood this implicitly created and then
|
||||
consumed an iterator, but we glossed over how exactly that works until now.
|
||||
The iterator is stored in the `v1_iter` variable. Once we’ve created an
|
||||
iterator, we can use it in a variety of ways. In Listing 3-5 in Chapter 3, we
|
||||
iterated over an array using a `for` loop to execute some code on each of its
|
||||
items. Under the hood this implicitly created and then consumed an iterator,
|
||||
but we glossed over how exactly that works until now.
|
||||
|
||||
The example in Listing 13-11 separates the creation of the iterator from the
|
||||
use of the iterator in the `for` loop. The iterator is stored in the `v1_iter`
|
||||
variable, and no iteration takes place at that time. When the `for` loop is
|
||||
called using the iterator in `v1_iter`, each element in the iterator is used in
|
||||
one iteration of the loop, which prints out each value.
|
||||
In the example in Listing 13-11, we separate the creation of the iterator from
|
||||
the use of the iterator in the `for` loop. When the `for` loop is called using
|
||||
the iterator in `v1_iter`, each element in the iterator is used in one
|
||||
iteration of the loop, which prints out each value.
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-11/src/main.rs:here}}
|
||||
|
@ -129,16 +129,15 @@ ownership of the iterator we call it on.
|
|||
|
||||
### Methods that Produce Other Iterators
|
||||
|
||||
Other methods defined on the `Iterator` trait, known as *iterator adaptors*,
|
||||
allow you to change iterators into different kinds of iterators. You can chain
|
||||
multiple calls to iterator adaptors to perform complex actions in a readable
|
||||
way. But because all iterators are lazy, you have to call one of the consuming
|
||||
adaptor methods to get results from calls to iterator adaptors.
|
||||
*Iterator adaptors* are methods defined on the `Iterator` trait that don’t
|
||||
consume the iterator. Instead, they produce different iterators by changing
|
||||
some aspect of the original iterator.
|
||||
|
||||
Listing 13-14 shows an example of calling the iterator adaptor method `map`,
|
||||
which takes a closure to call on each item to produce a new iterator. The
|
||||
closure here creates a new iterator in which each item from the vector has been
|
||||
incremented by 1. However, this code produces a warning:
|
||||
Listing 13-17 shows an example of calling the iterator adaptor method `map`,
|
||||
which takes a closure to call on each item as the items are iterated through.
|
||||
The `map` method returns a new iterator that produces the modified items. The
|
||||
closure here creates a new iterator in which each item from the vector will be
|
||||
incremented by 1:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -149,7 +148,7 @@ incremented by 1. However, this code produces a warning:
|
|||
<span class="caption">Listing 13-14: Calling the iterator adaptor `map` to
|
||||
create a new iterator</span>
|
||||
|
||||
The warning we get is this:
|
||||
However, this code produces a warning:
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch13-functional-features/listing-13-14/output.txt}}
|
||||
|
@ -159,9 +158,10 @@ The code in Listing 13-14 doesn’t do anything; the closure we’ve specified
|
|||
never gets called. The warning reminds us why: iterator adaptors are lazy, and
|
||||
we need to consume the iterator here.
|
||||
|
||||
To fix this and consume the iterator, we’ll use the `collect` method, which we
|
||||
used in Chapter 12 with `env::args` in Listing 12-1. This method consumes the
|
||||
iterator and collects the resulting values into a collection data type.
|
||||
To fix this warning and consume the iterator, we’ll use the `collect` method,
|
||||
which we used in Chapter 12 with `env::args` in Listing 12-1. This method
|
||||
consumes the iterator and collects the resulting values into a collection data
|
||||
type.
|
||||
|
||||
In Listing 13-15, we collect the results of iterating over the iterator that’s
|
||||
returned from the call to `map` into a vector. This vector will end up
|
||||
|
@ -182,14 +182,19 @@ on each item. This is a great example of how closures let you customize some
|
|||
behavior while reusing the iteration behavior that the `Iterator` trait
|
||||
provides.
|
||||
|
||||
You can chain multiple calls to iterator adaptors to perform complex actions in
|
||||
a readable way. But because all iterators are lazy, you have to call one of the
|
||||
consuming adaptor methods to get results from calls to iterator adaptors.
|
||||
|
||||
### Using Closures that Capture Their Environment
|
||||
|
||||
Now that we’ve introduced iterators, we can demonstrate a common use of
|
||||
closures that capture their environment by using the `filter` iterator adaptor.
|
||||
The `filter` method on an iterator takes a closure that takes each item from
|
||||
the iterator and returns a Boolean. If the closure returns `true`, the value
|
||||
will be included in the iterator produced by `filter`. If the closure returns
|
||||
`false`, the value won’t be included in the resulting iterator.
|
||||
Many iterator adapters take closures as arguments, and commonly the closures
|
||||
we’ll specify as arguments to iterator adapters will be closures that capture
|
||||
their environment. For this example, we’ll use the `filter` method that takes a
|
||||
closure. The closure gets an item from the iterator and returns a Boolean. If
|
||||
the closure returns `true`, the value will be included in the iteration
|
||||
produced by `filter`. If the closure returns `false`, the value won’t be
|
||||
included.
|
||||
|
||||
In Listing 13-16, we use `filter` with a closure that captures the `shoe_size`
|
||||
variable from its environment to iterate over a collection of `Shoe` struct
|
||||
|
|
|
@ -50,9 +50,9 @@ Open your I/O project’s *src/main.rs* file, which should look like this:
|
|||
{{#rustdoc_include ../listings/ch13-functional-features/listing-12-24-reproduced/src/main.rs:ch13}}
|
||||
```
|
||||
|
||||
We’ll change the start of the `main` function that we had in Listing 12-24 to
|
||||
the code in Listing 13-18. This won’t compile until we update `Config::build` as
|
||||
well.
|
||||
We’ll first change the start of the `main` function that we had in Listing
|
||||
12-24 to the code in Listing 13-18, which this time uses an iterator. This
|
||||
won’t compile until we update `Config::build` as well.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -158,6 +158,8 @@ into another vector with `collect`. Much simpler! Feel free to make the same
|
|||
change to use iterator methods in the `search_case_insensitive` function as
|
||||
well.
|
||||
|
||||
### Choosing Between Loops or Iterators
|
||||
|
||||
The next logical question is which style you should choose in your own code and
|
||||
why: the original implementation in Listing 13-21 or the version using
|
||||
iterators in Listing 13-22. Most Rust programmers prefer to use the iterator
|
||||
|
|
Loading…
Reference in New Issue