Propagate edits to ch13 to src

This commit is contained in:
Carol (Nichols || Goulding) 2022-06-10 21:40:50 -04:00
parent b24cc4d6b7
commit daa268a0cd
No known key found for this signature in database
GPG Key ID: E907EE5A736F87D4
5 changed files with 190 additions and 157 deletions

View File

@ -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)

View File

@ -14,11 +14,11 @@ More specifically, well 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: theyre 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: theyre faster than
you might think!)
Other Rust features, such as pattern matching and enums, which weve covered in
other chapters, are influenced by the functional style as well. Mastering
Weve 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 well devote this entire chapter to them.
code, well devote this entire chapter to them.

View File

@ -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
Rusts 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 theyre defined. Well
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 theyre defined.
Well 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 were going to examine is that closures can
capture values from the environment theyre defined in for later use. Heres
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 hasnt specified a favorite color, they get the color that the
company currently has the most of.
Well first examine how we can use closures to capture values from the
environment theyre defined in for later use. Heres 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 hasnt 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, were going to use an
enum called `ShirtColor` that has the variants `Red` and `Blue`. The
companys 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 companys
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
youve 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, weve stuck to concepts youve 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 weve 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()`. Were 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 weve passed a closure that calls
`self.most_stocked()` on the current `Inventory` instance. The standard library
didnt 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 dont
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
theyre 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 arent used in an exposed
interface like this: theyre 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, arent used
in an exposed interface like this: theyre 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, were 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. Weve 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. Weve 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 theyre 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 theyre 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 isnt very useful except for the purposes of this
example. Note that we havent 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, well get an error.
example. Note that we havent added any type annotations to the definition.
Because there are no type annotations, we can call the closure with any type,
which weve done here with `String` the first time. If we then try to call
`example_closure` with an integer, well 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 theres 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 dont use
the closure again after that point, the mutable borrow ends. Between the
closure definition and the closure call, an immutable borrow to print isnt
allowed because no other borrows are allowed when theres a mutable borrow. Try
adding a `println!` there to see what error message you get!
mutable reference to `list`. We dont 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 isnt allowed because no other
borrows are allowed when theres 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 doesnt 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 its owned by the new thread. Well have more examples of `move`
data so that its owned by the new thread. Well 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 dont 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 dont move captured values out of their body
and that dont 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 dont
capture anything from their environment implement `Fn`.
and that dont 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.
Lets 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 lets 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` doesnt 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 wont
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 wont 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 doesnt 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 closures
environment, into the `sort_operations` vector. The closure captures `value`
attempts to do this counting by pushing `value`a `String` from the closures
environmentinto 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 wouldnt 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 doesnt
move values out of the environment. If were 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, well 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

View File

@ -17,16 +17,16 @@ useful.
<span class="caption">Listing 13-10: Creating an iterator</span>
Once weve 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 weve 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 dont
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 doesnt do anything; the closure weve 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, well 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, well 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 thats
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 weve 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 wont be included in the resulting iterator.
Many iterator adapters take closures as arguments, and commonly the closures
well specify as arguments to iterator adapters will be closures that capture
their environment. For this example, well 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 wont 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

View File

@ -50,9 +50,9 @@ Open your I/O projects *src/main.rs* file, which should look like this:
{{#rustdoc_include ../listings/ch13-functional-features/listing-12-24-reproduced/src/main.rs:ch13}}
```
Well change the start of the `main` function that we had in Listing 12-24 to
the code in Listing 13-18. This wont compile until we update `Config::build` as
well.
Well 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
wont 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