diff --git a/second-edition/src/ch13-00-functional-features.md b/second-edition/src/ch13-00-functional-features.md
index 7c2ea0f26..77fd72a41 100644
--- a/second-edition/src/ch13-00-functional-features.md
+++ b/second-edition/src/ch13-00-functional-features.md
@@ -2,12 +2,12 @@
Rust’s design has taken inspiration from a lot of existing languages and
techniques, and one significant influence is *functional programming*.
-Programming in a functional style often includes using functions as values in
-arguments or return values of other functions, assigning functions to variables
-for later execution, and so forth. We won’t debate here the issue of what,
-exactly, functional programming is or is not, but will instead show off some
-features of Rust that are similar to features in many languages often referred
-to as functional.
+Programming in a functional style often includes using functions as values, by
+passing them in arguments, returning them from other functions, assigning them
+to variables for later execution, and so forth. We won’t debate here the issue
+of what, exactly, functional programming is or is not, but will instead show
+off some features of Rust that are similar to features in many languages often
+referred to as functional.
More specifically, we’re going to cover:
diff --git a/second-edition/src/ch13-01-closures.md b/second-edition/src/ch13-01-closures.md
index 02d2a6d14..7a699123c 100644
--- a/second-edition/src/ch13-01-closures.md
+++ b/second-edition/src/ch13-01-closures.md
@@ -1,30 +1,31 @@
## Closures: Anonymous Functions that can Capture their Environment
-Rust’s *closures* are anonymous functions that 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 are allowed to capture values from the scope in which they
-are called. We’re going to demonstrate how these features of closures allow for
-code reuse and customization of behavior.
+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 are able to capture values from the scope in which they are called.
+We’re going to demonstrate how these features of closures allow for code reuse
+and customization of behavior.
### Creating an Abstraction of Behavior Using a Closure
-Let’s work on an example that will show a situation where storing a closure to
-be executed at a later time is useful. We’ll talk about the syntax of closures,
-type inference, and traits along the way.
+Let’s work on an example of a situation in which it’s useful to store a closure
+to be executed at a later time. We’ll talk about the syntax of closures, type
+inference, and traits along the way.
-The hypothetical situation is this: we’re working at a startup that’s making an
-app to generate custom exercise workout plans. The backend is written in Rust,
-and the algorithm that generates the workout plan takes into account many
-different factors like the app user’s age, their Body Mass Index, their
-preferences, their recent workouts, and an intensity number they specify. The
-actual algorithm used isn’t important in this example; what’s important is that
-this calculation takes a few seconds. We only want to call this algorithm if we
-need to, and we only want to call it once, so that we aren’t making the user
-wait more than they need to. We’re going to simulate calling this hypothetical
-algorithm by calling the `simulated_expensive_calculation` function shown in
-Listing 13-1 instead, which will print `calculating slowly...`, wait for two
-seconds, and then return whatever number we passed in:
+The hypothetical situation is this: we work at a startup that’s making an app
+to generate custom exercise workout plans. The backend is written in Rust, and
+the algorithm that generates the workout plan takes into account many different
+factors, like the app user’s age, Body Mass Index, preferences, recent
+workouts, and an intensity number they specify. The actual algorithm used isn’t
+important in this example; what’s important is that this calculation takes a
+few seconds. We only want to call this algorithm when we need to, and only call
+it once, so we aren’t making the user wait more than necessary.
+
+We’ll simulate calling this hypothetical algorithm with the
+`simulated_expensive_calculation` function shown in Listing 13-1, which will
+print `calculating slowly...`, wait for two seconds, and then return whatever
+number we passed in:
Filename: src/main.rs
@@ -39,31 +40,25 @@ fn simulated_expensive_calculation(intensity: i32) -> i32 {
}
```
-Listing 13-1: A function we’ll use to stand in for a
-hypothetical calculation that takes about two seconds to run
+Listing 13-1: A function to stand in for a hypothetical
+calculation that takes about two seconds to run
-Next, we have a `main` function that contains the parts of the workout app that
-are important for this example. This represents the code that the app would
-call when a user asks for a workout plan. Because the interaction with the
-app’s frontend isn’t relevant to the use of closures, we’re going to hardcode
-values representing inputs to our program and print the outputs.
+Next, we have a `main` function that contains the parts of the workout app
+important for this example. This represents the code that the app would call
+when a user asks for a workout plan. Because the interaction with the app’s
+frontend isn’t relevant to the use of closures, we’re going to hardcode values
+representing inputs to our program and print the outputs.
-The inputs to the program are:
+The required inputs are:
-- An `intensity` number from the user, specified when they request a workout,
- so they can indicate whether they’d like a low intensity workout or a high
+* **An intensity number from the user**, specified when they request a
+ workout to indicate whether they’d like a low intensity workout or a high
intensity workout
-- A random number that will generate some variety in the workout plans
+* **A random number** that will generate some variety in the workout plans
-The output the program prints will be the recommended workout plan.
+The output will be the recommended workout plan.
-Listing 13-2 shows the `main` function we’re going to use. We’ve hardcoded the
-variable `simulated_user_specified_value` to 10 and the variable
-`simulated_random_number` to 7 for simplicity’s sake; in an actual program we’d
-get the intensity number from the app frontend and we’d use the `rand` crate to
-generate a random number like we did in the Guessing Game example in Chapter 2.
-The `main` function calls a `generate_workout` function with the simulated
-input values:
+Listing 13-2 shows the `main` function we’re going to use.
Filename: src/main.rs
@@ -72,19 +67,28 @@ fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;
- generate_workout(simulated_user_specified_value, simulated_random_number);
+ generate_workout(
+ simulated_user_specified_value,
+ simulated_random_number
+ );
}
# fn generate_workout(intensity: i32, random_number: i32) {}
```
-Listing 13-2: A `main` function containing hardcoded
-values to simulate user input and random number generation inputs to the
-`generate_workout` function
+Listing 13-2: A `main` function with hardcoded values to
+simulate user input and random number generation
-That’s the context of what we’re working on. The `generate_workout` function in
-Listing 13-3 contains the business logic of the app that we’re most concerned
-with in this example. The rest of the code changes in this example will be made
-to this function:
+We’ve hardcoded the variable `simulated_user_specified_value` to 10 and the
+variable `simulated_random_number` to 7 for simplicity’s sake; in an actual
+program we’d get the intensity number from the app frontend and we’d use the
+`rand` crate to generate a random number like we did in the Guessing Game
+example in Chapter 2. The `main` function calls a `generate_workout` function
+with the simulated input values.
+
+There’s the context, so let’s get to the algorithm. The `generate_workout`
+function in Listing 13-3 contains the business logic of the app that we’re most
+concerned with in this example. The rest of the code changes in this example
+will be made to this function:
Filename: src/main.rs
@@ -115,46 +119,46 @@ fn generate_workout(intensity: i32, random_number: i32) {
println!(
"Today, run for {} minutes!",
simulated_expensive_calculation(intensity)
- )
+ );
}
}
}
```
-Listing 13-3: The business logic of the program that
-prints the workout plans based on the inputs and calls to the
-`simulated_expensive_calculation` function
+Listing 13-3: The business logic that prints the workout
+plans based on the inputs and calls to the `simulated_expensive_calculation`
+function
The code in Listing 13-3 has multiple calls to the slow calculation function.
The first `if` block calls `simulated_expensive_calculation` twice, the `if`
-inside the outer `else` doesn’t call it at all, and the code inside the `else`
-case inside the outer `else` calls it once.
+inside the outer `else` doesn’t call it at all, and the code inside the
+second `else` case calls it once.
The desired behavior of the `generate_workout` function is to first check if
the user wants a low intensity workout (indicated by a number less than 25) or
-a high intensity workout (25 or more). Low intensity workout plans will
-recommend a number of pushups and situps based on the complex algorithm we’re
-simulating with the `simulated_expensive_calculation` function, which needs the
-intensity number as an input.
+a high intensity workout (25 or more).
+
+Low intensity workout plans will recommend a number of pushups and situps based
+on the complex algorithm we’re simulating.
If the user wants a high intensity workout, there’s some additional logic: if
the value of the random number generated by the app happens to be 3, the app
-will recommend a break and hydration instead. If not, the user will get a high
-intensity workout of a number of minutes of running that comes from the complex
-algorithm.
+will recommend a break and hydration. If not, the user will get a number of
+minutes of running based on the complex algorithm.
-The data science team has let us know that there are going to be some changes
-to the way we have to call the algorithm. To simplify the update when those
-changes happen, we would like to refactor this code to have only a single call
-to the `simulated_expensive_calculation` function. We also want to get rid of
-the spot where we’re currently calling the function twice unnecessarily, and
-we don’t want to add any other calls to that function in the process. That is,
-we don’t want to call it if we’re in the case where the result isn’t needed at
-all, and we still want to call it only once in the last case.
+The data science team has let us know that we’ll have to make some changes to
+the way we call the algorithm in the future. To simplify the update when those
+changes happen, we want to refactor this code so it only calls the
+`simulated_expensive_calculation` function once. We also want to cut the place
+where we’re currently calling the function twice unnecessarily without adding
+any other calls to that function in the process. That is, we don’t want to call
+it if the result isn’t needed, and we still want to call it only once.
-There are many ways we could restructure this program. The way we’re going to
-try first is extracting the duplicated call to the expensive calculation
-function into a variable, as shown in Listing 13-4:
+#### Refactoring Using Functions
+
+There are many ways we could restructure this program. First we’ll try
+extracting the duplicated call to the expensive calculation function into a
+variable, as shown in Listing 13-4:
Filename: src/main.rs
@@ -188,15 +192,15 @@ fn generate_workout(intensity: i32, random_number: i32) {
println!(
"Today, run for {} minutes!",
expensive_result
- )
+ );
}
}
}
```
Listing 13-4: Extracting the calls to
-`simulated_expensive_calculation` to one place before the `if` blocks and
-storing the result in the `expensive_result` variable
+`simulated_expensive_calculation` to one place and storing the result in the
+`expensive_result` variable
This change unifies all the calls to `simulated_expensive_calculation` and
solves the problem of the first `if` block calling the function twice
@@ -204,15 +208,14 @@ unnecessarily. Unfortunately, we’re now calling this function and waiting for
the result in all cases, which includes the inner `if` block that doesn’t use
the result value at all.
-We want to be able to specify some code in one place in our program, but then
-only execute that code if we actually need the result in some other place in
-our program. This is a use case for closures!
+We want to define code in one place in our program, but only *execute* that
+code where we actually need the result. This is a use case for closures!
-### Closures Store Code to be Executed Later
+#### Refactoring with Closures to Store Code for Later Execution
Instead of always calling the `simulated_expensive_calculation` function before
-the `if` blocks, we can define a closure and store the closure in a variable
-instead of the result as shown in Listing 13-5. We can actually choose to move
+the `if` blocks, we can define a closure and store the *closure* in a variable
+rather than storing the result, as shown in Listing 13-5. We can actually move
the whole body of `simulated_expensive_calculation` within the closure we’re
introducing here:
@@ -230,37 +233,34 @@ let expensive_closure = |num| {
# expensive_closure(5);
```
-Listing 13-5: Defining a closure with the body that was
-in the expensive function and store the closure in the `expensive_closure`
-variable
+Listing 13-5: Defining a closure and storing it in the
+`expensive_closure` variable
-The closure definition is the part after the `=` that we’re assigning to the
-variable `expensive_closure`. To define a closure, we start with a pair of
-vertical pipes (`|`). Inside the pipes is where we specify the parameters to
-the closure; this syntax was chosen because of its similarity to closure
-definitions in Smalltalk and Ruby. This closure has one parameter named `num`;
-if we had more than one parameter, we would separate them with commas, like
-`|param1, param2|`.
+The closure definition comes after the `=` to assign it to the variable
+`expensive_closure`. To define a closure, we start with a pair of vertical
+pipes (`|`), inside which we specify the parameters to the closure; this syntax
+was chosen because of its similarity to closure definitions in Smalltalk and
+Ruby. This closure has one parameter named `num`; if we had more than one
+parameter, we would separate them with commas, like `|param1, param2|`.
-After the parameters, we put curly braces that hold the body of the closure.
-The curly braces are optional if the closure body only has one line. After the
-curly braces, we need a semicolon to go with the `let` statement. The value
-returned from the last line in the closure body (`num`), since that line
-doesn’t end in a semicolon, will be the value returned from the closure when
-it’s called, just like in function bodies.
+After the parameters, we place curly braces that hold the body of the
+closure—these are optional if the closure body is a single expression. The end
+of the closure, after the curly braces, needs a semicolon to complete the `let`
+statement. The value returned from the last line in the closure body (`num`)
+will be the value returned from the closure when it’s called, since that line
+doesn’t end in a semicolon; just like in function bodies.
Note that this `let` statement means `expensive_closure` contains the
*definition* of an anonymous function, not the *resulting value* of calling the
-anonymous function. Recall the reason we’re using a closure is because we want
-to define the code to call at one point, store that code, and actually call it
-at a later point; the code we want to call is now stored in `expensive_closure`.
+anonymous function. Recall that we’re using a closure because we want to define
+the code to call at one point, store that code, and actually call it at a later
+point; the code we want to call is now stored in `expensive_closure`.
Now that we have the closure defined, we can change the code in the `if` blocks
-to call the closure in order to execute the code and get the resulting value.
-Calling a closure looks very similar to calling a function; we specify the
-variable name that holds the closure definition and follow it with parentheses
-containing the argument values we want to use for that call as shown in Listing
-13-6:
+to call the closure, in order to execute the code and get the resulting value.
+We call a closure like we do a function: we specify the variable name that
+holds the closure definition and follow it with parentheses containing the
+argument values we want to use, as shown in Listing 13-6:
Filename: src/main.rs
@@ -291,7 +291,7 @@ fn generate_workout(intensity: i32, random_number: i32) {
println!(
"Today, run for {} minutes!",
expensive_closure(intensity)
- )
+ );
}
}
}
@@ -300,42 +300,44 @@ fn generate_workout(intensity: i32, random_number: i32) {
Listing 13-6: Calling the `expensive_closure` we’ve
defined
-Now we’ve achieved the goal of unifying where the expensive calculation is
-called to one place, and we’re only executing that code where we need the
-results. However, we’ve reintroduced one of the problems from Listing 13-3:
-we’re still calling the closure twice in the first `if` block, which will call
-the expensive code twice and make the user wait twice as long as they need to.
-We could fix this problem by creating a variable local to that `if` block to
-hold the result of calling the closure, but there’s another solution we can use
-since we have a closure. We’ll get back to that solution in a bit; let’s first
-talk about why there aren’t type annotations in the closure definition and the
-traits involved with closures.
+Now the expensive calculation is called in only one place, and we’re only
+executing that code where we need the results.
+
+We have, however, reintroduced one of the problems from Listing 13-3: we’re
+still calling the closure twice in the first `if` block, which will call the
+expensive code twice and make the user wait twice as long as they need to. We
+could fix this problem by creating a variable local to that `if` block to hold
+the result of calling the closure, but closures provide us with another
+solution. We’ll get back to that solution in a bit; let’s first talk about why
+there aren’t type annotations in the closure definition and the traits involved
+with closures.
### Closure Type Inference and Annotation
-Closures differ from functions defined with the `fn` keyword in a few
-ways. The first is that closures don’t require you to annotate the types of the
+Closures differ from functions defined with the `fn` keyword in a few ways. The
+first is that closures don’t 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 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. Closures aren’t used in an exposed interface like this,
-though: they’re stored in variables and used without naming them and exposing
-them to be invoked by users of our library.
+Type annotations are required on functions because they 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. Closures aren’t used in an exposed interface like this, though:
+they’re stored in variables and used without naming them and exposing them to
+users of our library.
Additionally, closures are usually short and only relevant within a narrow
context rather than in any arbitrary scenario. Within these limited contexts,
the compiler is reliably able to infer the types of the parameters and return
-type similarly to how it’s able to infer the types of most variables. Being
-forced to annotate the types in these small, anonymous functions would be
-annoying and largely redundant with the information the compiler already has
+type, similar to how it’s able to infer the types of most variables.
+
+Making programmers annotate the types in these small, anonymous functions would
+be annoying and largely redundant with the information the compiler already has
available.
Like variables, we can choose to add type annotations if we want to increase
-explicitness and clarity in exchange for being more verbose than is strictly
+explicitness and clarity at the cost of being more verbose than is strictly
necessary; annotating the types for the closure we defined in Listing 13-4
-would look like the definition shown here in Listing 13-7:
+would look like the definition shown in Listing 13-7:
Filename: src/main.rs
@@ -357,8 +359,8 @@ The syntax of closures and functions looks more similar with type annotations.
Here’s a vertical comparison of the syntax for the definition of a function
that adds one to its parameter, and a closure that has the same behavior. We’ve
added some spaces here to line up the relevant parts). This illustrates how
-closure syntax is similar to function syntax except for the use of pipes rather
-than parentheses and the amount of syntax that is optional:
+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: i32) -> i32 { x + 1 }
@@ -370,16 +372,18 @@ 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 braces that are
-optional since the closure body only has one line. These are all valid
+optional, since the closure body only has one expression. These are all valid
definitions that will produce the same behavior when they’re called.
Closure definitions will have one concrete type inferred for each of their
parameters and for their return value. For instance, Listing 13-8 shows the
-definition of a short closure that just returns the value it gets 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 an `i32` the second time, we’ll get an error:
+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 an
+`i32` the second time, we’ll get an error:
Filename: src/main.rs
@@ -412,29 +416,30 @@ infers the type of `x` and the return type of the closure to be `String`. Those
types are then locked in to the closure in `example_closure`, and we get a type
error if we try to use a different type with the same closure.
-### Using Closures with Generic Parameters and the `Fn` Traits
+### Storing Closures Using Generic Parameters and the `Fn` Traits
Returning to our workout generation app, in Listing 13-6 we left our code still
-calling the expensive calculation closure more times than it needs to. In each
-place throughout our code, if we need the results of the expensive closure more
-than once, we could save the result in a variable for reuse and use the
-variable instead of calling the closure again. This could be a lot of repeated
-code saving the results in a variety of places.
+calling the expensive calculation closure more times than it needs to. One
+option to solve this issue is to save the result of the expensive closure in a
+variable for reuse and use the variable instead in each place we need the
+result instead of calling the closure again. This method, though, could result
+in a lot of repeated code.
-However, because we have a closure for the expensive calculation, we have
-another solution available to us. We can create a struct that will hold the
-closure and the resulting value of calling the closure. The struct will only
-execute the closure if we need the resulting value, and it will cache the
-resulting value so that the rest of our code doesn’t have to be responsible for
-saving and reusing the result. You may know this pattern as *memoization* or
-*lazy evaluation*.
+Because we have a closure for the expensive calculation, we have another
+solution available to us. We can create a struct that will hold the closure and
+the resulting value of calling the closure. The struct will only execute the
+closure if we need the resulting value, and it will cache the resulting value
+so that the rest of our code doesn’t have to be responsible for saving and
+reusing the result. You may know this pattern as *memoization* or *lazy
+evaluation*.
In order to make a struct that holds a closure, we need to be able to specify
-the type of the closure. Each closure instance has its own unique anonymous
-type: that is, even if two closures have the same signature, their types are
-still considered to be different. In order to define structs, enums, or
-function parameters that use closures, we use generics and trait bounds like we
-discussed in Chapter 10.
+the type of the closure, because a struct definition needs to know the types of
+each of its fields. Each closure instance has its own unique anonymous type:
+that is, even if two closures have the same signature, their types are still
+considered different. In order to define structs, enums, or function parameters
+that use closures, we use generics and trait bounds like we discussed in
+Chapter 10.
The `Fn` traits are provided by the standard library. All closures implement
one of the traits `Fn`, `FnMut`, or `FnOnce`. We’ll discuss the difference
@@ -442,9 +447,9 @@ between these traits in the next section on capturing the environment; in this
example, we can use the `Fn` trait.
We add types to the `Fn` trait bound to represent the types of the parameters
-and return values that the closures must have in order to match this trait
-bound. In this case, our closure has a parameter of type `i32` and returns an
-`i32`, so the trait bound we specify is `Fn(i32) -> i32`.
+and return values the closures must have in order to match this trait bound. In
+this case, our closure has a parameter of type `i32` and returns an `i32`, so
+the trait bound we specify is `Fn(i32) -> i32`.
Listing 13-9 shows the definition of the `Cacher` struct that holds a closure
and an optional result value:
@@ -464,20 +469,20 @@ struct Cacher
closure in `calculation` and an optional result in `value`
The `Cacher` struct has a `calculation` field of the generic type `T`. The
-trait bounds on `T` specify that `T` is a closure by using the `Fn` trait. Any
-closure we want to store in the `calculation` field of a `Cacher` instance must
-have one `i32` parameter (specified within the parentheses after `Fn`) and must
-return an `i32` (specified after the `->`).
+trait bounds on `T` specify that it’s a closure by using the `Fn` trait. Any
+closure we want to store in the `calculation` field must have one `i32`
+parameter (specified within the parentheses after `Fn`) and must return an
+`i32` (specified after the `->`).
The `value` field is of type `Option`. Before we execute the closure,
-`value` will be `None`. If the code using a `Cacher` asks for the result of the
-closure, we’ll execute the closure at that time and store the result within a
-`Some` variant in the `value` field. Then if the code asks for the result of
-the closure again, instead of executing the closure again, we’ll return the
-result that we’re holding in the `Some` variant.
+`value` will be `None`. When code using a `Cacher` asks for the *result* of the
+closure, the `Cacher` will execute the closure at that time and store the
+result within a `Some` variant in the `value` field. Then if the code asks for
+the result of the closure again, instead of executing the closure again, the
+`Cacher` will return the result held in the `Some` variant.
-The logic around the `value` field that we’ve just described is defined in
-Listing 13-10:
+The logic around the `value` field we’ve just described is defined in Listing
+13-10:
Filename: src/main.rs
@@ -512,17 +517,17 @@ impl Cacher
}
```
-Listing 13-10: Implementations on `Cacher` of an
-associated function named `new` and a method named `value` that manage the
-caching logic
+Listing 13-10: The caching logic of `Cacher`
-The fields on the `Cacher` struct are private since we want `Cacher` to manage
-their values rather than letting the calling code potentially change the values
-in these fields directly. The `Cacher::new` function takes a generic parameter
-`T`, which we’ve defined in the context of the `impl` block to have the same
-trait bound as the `Cacher` struct. `Cacher::new` returns a `Cacher` instance
-that holds the closure specified in the `calculation` field and a `None` value
-in the `value` field, since we haven’t executed the closure yet.
+We want `Cacher` to manage the struct fields’ values, rather than letting the
+calling code potentially change the values in these fields directly, so these
+fields are private.
+
+The `Cacher::new` function takes a generic parameter `T`, which we’ve defined
+as having the same trait bound as the `Cacher` struct. Then `Cacher::new`
+returns a `Cacher` instance that holds the closure specified in the
+`calculation` field and a `None` value in the `value` field, since we haven’t
+executed the closure yet.
When the calling code wants the result of evaluating the closure, instead of
calling the closure directly, it will call the `value` method. This method
@@ -594,7 +599,7 @@ fn generate_workout(intensity: i32, random_number: i32) {
println!(
"Today, run for {} minutes!",
expensive_result.value(intensity)
- )
+ );
}
}
}
@@ -607,19 +612,22 @@ Instead of saving the closure in a variable directly, we save a new instance of
`Cacher` that holds the closure. Then, in each place we want the result, we
call the `value` method on the `Cacher` instance. We can call the `value`
method as many times as we want, or not call it at all, and the expensive
-calculation will be run a maximum of once. Try running this program with the
-`main` function from Listing 13-2, and change the values in the
-`simulated_user_specified_value` and `simulated_random_number` variables to
-verify that in all of the cases in the various `if` and `else` blocks,
-`calculating slowly...` printed by the closure only shows up once and only when
-needed.
+calculation will be run a maximum of once.
-The `Cacher` takes care of the logic necessary to ensure we aren’t calling the
+Try running this program with the `main` function from Listing 13-2. Change the
+values in the `simulated_user_specified_value` and `simulated_random_number`
+variables to verify that in all of the cases in the various `if` and `else`
+blocks, `calculating slowly...` only shows up once and only when needed. The
+`Cacher` takes care of the logic necessary to ensure we aren’t calling the
expensive calculation more than we need to, so that `generate_workout` can
-focus on the business logic. Caching values is a more generally useful behavior
-that we might want to use in other parts of our code with other closures as
-well. However, there are a few problems with the current implementation of
-`Cacher` that would make reusing it in different contexts difficult.
+focus on the business logic.
+
+### Limitations of the `Cacher` Implementation
+
+Caching values is a generally useful behavior that we might want to use in
+other parts of our code with different closures. However, there are a few
+problems with the current implementation of `Cacher` that would make reusing it
+in different contexts difficult.
The first problem is a `Cacher` instance assumes it will always get the same
value for the parameter `arg` to the `value` method. That is, this test of
@@ -638,9 +646,9 @@ fn call_with_different_values() {
```
This test creates a new `Cacher` instance with a closure that returns the value
-passed into it. We call the `value` method on this `Cacher` instance with
-an `arg` value of 1 and then an `arg` value of 2, and we expect that the call
-to `value` with the `arg` value of 2 returns 2.
+passed into it. We call the `value` method on this `Cacher` instance with an
+`arg` value of 1 and then an `arg` value of 2, and we expect that the call to
+`value` with the `arg` value of 2 should return 2.
Run this with the `Cacher` implementation from Listing 13-9 and Listing 13-10
and the test will fail on the `assert_eq!` with this message:
@@ -651,30 +659,29 @@ thread 'call_with_different_arg_values' panicked at 'assertion failed:
```
The problem is that the first time we called `c.value` with 1, the `Cacher`
-instance saved `Some(1)` in `self.value`. After that, no matter what we pass
-in to the `value` method, it will always return 1.
+instance saved `Some(1)` in `self.value`. After that, no matter what we pass in
+to the `value` method, it will always return 1.
Try modifying `Cacher` to hold a hash map rather than a single value. The keys
of the hash map will be the `arg` values that are passed in, and the values of
the hash map will be the result of calling the closure on that key. Instead of
looking at whether `self.value` directly has a `Some` or a `None` value, the
-`value` function will look up the `arg` in the hash map and return the value if
-it’s present. If it’s not present, the `Cacher` will call the closure and save
-the resulting value in the hash map associated with its `arg` value.
+`value` function will look up the `arg` in the hash map and return the value,
+if it’s present. If it’s not present, the `Cacher` will call the closure and
+save the resulting value in the hash map associated with its `arg` value.
-Another problem with the current `Cacher` implementation that restricts its use
-is that it only accepts closures that take one parameter of type `i32` and
-return an `i32`. We might want to be able to cache the results of closures that
-take a string slice as an argument and return `usize` values, for example. Try
-introducing more generic parameters to increase the flexibility of the `Cacher`
-functionality.
+Another problem with the current `Cacher` implementation is that it only
+accepts closures that take one parameter of type `i32` and return an `i32`. We
+might want to cache the results of closures that take a string slice and return
+`usize` values, for example. To fix this issue, try introducing more generic
+parameters to increase the flexibility of the `Cacher` functionality.
### Closures Can Capture Their Environment
In the workout generator example, we only used closures as inline anonymous
-functions. Closures have an additional ability we can use that functions don’t
-have, however: they can capture their environment and access variables from the
-scope in which they’re defined.
+functions. Closures have an additional ability that functions don’t have,
+however: they can capture their environment and access variables from the scope
+in which they’re defined.
Listing 13-12 has an example of a closure stored in the variable `equal_to_x`
that uses the variable `x` from the closure’s surrounding environment:
@@ -729,39 +736,40 @@ closure form instead
The compiler even reminds us that this only works with closures!
-When a closure captures a value from its environment, the closure uses memory
-to store the values for use in the closure body. This use of memory is overhead
-that we don’t want to pay for in the more common case where we want to execute
-code that doesn’t capture its environment. Because functions are never allowed
-to capture their environment, defining and using functions will never incur
-this overhead.
+When a closure captures a value from its environment, it uses memory to store
+the values for use in the closure body. This use of memory is overhead that we
+don’t want to pay in more common cases, where we want to execute code that
+doesn’t capture its environment. Because functions are never allowed to capture
+their environment, defining and using functions will never incur this overhead.
Closures can capture values from their environment in three ways, which
directly map to the three ways a function can take a parameter: taking
-ownership, borrowing immutably, and borrowing mutably. These ways of capturing
-values are encoded in the three `Fn` traits as follows:
+ownership, borrowing immutably, and borrowing mutably. These are encoded in the
+three `Fn` traits as follows:
-* `FnOnce` consumes the variables it captures from its enclosing scope (the
- enclosing scope is called the closure’s *environment*). In order to consume
- the captured variables, the closure must therefore take ownership of these
- variables and moves them into the closure when the closure is defined. The
- `Once` part of the name is because the closure can’t take ownership of the
- same variables more than once, so it can only be called one time.
+* `FnOnce` consumes the variables it captures from its enclosing scope, known
+ as the closure’s *environment*. In order to consume the captured variables,
+ the closure must take ownership of these variables and move them into the
+ closure when it is defined. The `Once` part of the name is because the
+ closure can’t take ownership of the same variables more than once, so it can
+ only be called one time.
* `Fn` borrows values from the environment immutably.
* `FnMut` can change the environment since it mutably borrows values.
-When we create a closure, Rust infers how we want to reference the environment
-based on how the closure uses the values from the environment. In Listing
-13-12, the `equal_to_x` closure borrows `x` immutably (so `equal_to_x` has the
-`Fn` trait) since the body of the closure only needs to read the value in `x`.
+When we create a closure, Rust infers which to use based on how the closure
+uses the values from the environment. In Listing 13-12, the `equal_to_x`
+closure borrows `x` immutably (so `equal_to_x` has the `Fn` trait) since the
+body of the closure only needs to read the value in `x`.
If we want to force the closure to take ownership of the values it uses in the
environment, we can use the `move` keyword before the parameter list. This is
mostly useful when passing a closure to a new thread in order to move the data
-to be owned by the new thread. We’ll have more examples of `move` closures in
-Chapter 16 when we talk about concurrency, but for now here’s the code from
-Listing 13-12 with the `move` keyword added to the closure definition and using
-vectors instead of integers, since integers can be copied rather than moved:
+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, but for now here’s the code from Listing 13-12 with the `move`
+keyword added to the closure definition and using vectors instead of integers,
+since integers can be copied rather than moved:
Filename: src/main.rs
@@ -795,9 +803,10 @@ error[E0382]: use of moved value: `x`
implement the `Copy` trait
```
-The `x` value is moved into the closure when the closure is defined because of
-the `move` keyword. The closure then has ownership of `x`, and `main` isn’t
-allowed to use `x` anymore. Removing the `println!` will fix this example.
+The `x` value is moved into the closure when the closure is defined, because we
+added the `move` keyword. The closure then has ownership of `x`, and `main`
+isn’t allowed to use `x` anymore in the `println!` statement. Removing
+`println!` will fix this example.
Most of the time when specifying one of the `Fn` trait bounds, you can start
with `Fn` and the compiler will tell you if you need `FnMut` or `FnOnce` based
diff --git a/second-edition/src/ch13-02-iterators.md b/second-edition/src/ch13-02-iterators.md
index 7770f54af..e91e6829e 100644
--- a/second-edition/src/ch13-02-iterators.md
+++ b/second-edition/src/ch13-02-iterators.md
@@ -1,15 +1,15 @@
## Processing a Series of Items with Iterators
The iterator pattern allows you to perform some task on a sequence of items in
-turn. An *iterator* is responsible for the logic around iterating over each item
-in the sequence and determining when the sequence has finished. When we use
-iterators, we don’t have to reimplement that logic ourselves.
+turn. An *iterator* is responsible for the logic of iterating over each item
+and determining when the sequence has finished. When we use iterators, we don’t
+have to reimplement that logic ourselves.
-In Rust, iterators are *lazy*, which means they have no effect until we call
-methods on them that consume the iterator to use it up. For example, the code
-in Listing 13-13 creates an iterator over the items in the vector `v1` by
-calling the `iter` method defined on `Vec`. This code by itself doesn’t do
-anything useful:
+In Rust, iterators are *lazy*, meaning they have no effect until we call
+methods that consume the iterator to use it up. For example, the code in
+Listing 13-13 creates an iterator over the items in the vector `v1` by calling
+the `iter` method defined on `Vec`. This code by itself doesn’t do anything
+useful:
```rust
let v1 = vec![1, 2, 3];
@@ -19,11 +19,13 @@ let v1_iter = v1.iter();
Listing 13-13: Creating an iterator
-After creating an iterator, we can choose to use it in a variety of ways. In
-Listing 3-6, we actually used iterators with `for` loops to execute some code
-on each item, though we glossed over what the call to `iter` did until now. The
-example in Listing 13-14 separates the creation of the iterator from the use of
-the iterator in the `for` loop. The iterator is stored in the `v1_iter`
+Once we’ve created an iterator, we can choose to use it in a variety of ways.
+In Listing 3-6 from Chapter 3, we actually used iterators with `for` loops to
+execute some code on each item, though we glossed over what the call to `iter`
+did until now.
+
+The example in Listing 13-14 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. Once the `for` loop is
called using the iterator in `v1_iter`, then each element in the iterator is
used in one iteration of the loop, which prints out each value:
@@ -44,12 +46,13 @@ loop
In languages that don’t have iterators provided by their standard libraries, we
would likely write this same functionality by starting a variable at index 0,
using that variable to index into the vector to get a value, and incrementing
-the variable value in a loop until its value gets up to the total number of
-items in the vector. Iterators take care of all of that logic for us, which
-cuts down on the repetitive code we would have to write and potentially mess up.
-In addition, the way iterators are implemented gives us more flexibility to
-use the same logic with many different kinds of sequences, not just data
-structures that we can index into like vectors. Let’s see how iterators do that.
+the variable value in a loop until it gets to the total number of items in the
+vector.
+
+Iterators take care of all of that logic for us, cutting down on repetitive
+code we could potentially mess up. Iterators give us more flexibility to use
+the same logic with many different kinds of sequences, not just data structures
+we can index into like vectors. Let’s see how iterators do that.
### The `Iterator` trait and the `next` method
@@ -69,17 +72,18 @@ trait Iterator {
You’ll notice some new syntax that we haven’t covered yet: `type Item` and
`Self::Item`, which are defining an *associated type* with this trait. We’ll
talk about associated types in depth in Chapter 19, but for now, all you need
-to know is that this code says implementing `Iterator` trait requires that you
-also define an `Item` type, and this `Item` type is used in the return type of
-the `next` method. In other words, the `Item` type will be the type of element
-that’s returned from the iterator.
+to know is that this code says implementing the `Iterator` trait requires that
+you also define an `Item` type, and this `Item` type is used in the return type
+of the `next` method. In other words, the `Item` type will be the type returned
+from the iterator.
-The `next` method is the only method that the `Iterator` trait requires
-implementors of the trait to define. `next` returns one item of the iterator
-at a time wrapped in `Some`, and when iteration is over, it returns `None`.
-We can call the `next` method on iterators directly if we’d like; Listing 13-15
-has a test that demonstrates the values we’d get on repeated calls to `next`
-on the iterator created from the vector:
+The `Iterator` trait only requires implementors to define one method: the
+`next` method, which returns one item of the iterator at a time wrapped in
+`Some` and, when iteration is over, it returns `None`.
+
+We can call the `next` method on iterators directly; Listing 13-15 demonstrates
+what values are returned from repeated calls to `next` on the iterator created
+from the vector:
Filename: src/lib.rs
@@ -101,15 +105,15 @@ fn iterator_demonstration() {
iterator
Note that we needed to make `v1_iter` mutable: calling the `next` method on an
-iterator changes the iterator’s state that keeps track of where it is in the
-sequence. Put another way, this code *consumes*, or uses up, the iterator. Each
-call to `next` eats up an item from the iterator. We didn’t need to make
-`v1_iter` mutable when we used a `for` loop because the `for` loop took
-ownership of `v1_iter` and made `v1_iter` mutable behind the scenes.
+iterator changes state that keeps track of where it is in the sequence. Put
+another way, this code *consumes*, or uses up, the iterator. Each call to
+`next` eats up an item from the iterator. We didn’t need to make `v1_iter`
+mutable when we used a `for` loop because the loop took ownership of `v1_iter`
+and made it mutable behind the scenes.
Also note that the values we get from the calls to `next` are immutable
references to the values in the vector. The `iter` method produces an iterator
-over immutable references. If we wanted to create an iterator that takes
+over immutable references. If we want to create an iterator that takes
ownership of `v1` and returns owned values, we can call `into_iter` instead of
`iter`. Similarly, if we want to iterate over mutable references, we can call
`iter_mut` instead of `iter`.
@@ -123,13 +127,12 @@ the `Iterator` trait. Some of these methods call the `next` method in their
definition, which is why we’re required to implement the `next` method when
implementing the `Iterator` trait.
-The methods that call the `next` method are called *consuming adaptors*, since
-calling them uses up the iterator. An example of a consuming adaptor is the
-`sum` method. This method takes ownership of the iterator and iterates through
-the items by repeatedly calling `next`, thus consuming the iterator. As it
-iterates through each item, it adds each item to a running total and returns
-the total when iteration has completed. Listing 13-16 has a test illustrating a
-use of the `sum` method:
+Methods that call `next` are called *consuming adaptors*, because calling them
+uses up the iterator. One example is the `sum` method, which takes ownership of
+the iterator and iterates through the items by repeatedly calling `next`, thus
+consuming the iterator. As it iterates through, it adds each item to a running
+total and returns the total when iteration is complete. Listing 13-16 has a
+test illustrating a use of the `sum` method:
Filename: src/lib.rs
@@ -154,14 +157,16 @@ ownership of the iterator we call it on.
### Methods in the `Iterator` Trait that Produce Other Iterators
-Another kind of method defined on the `Iterator` trait are methods that produce
-other iterators. These methods are called *iterator adaptors* and allow us to
-change iterators into different kind of iterators. We can chain multiple calls
-to iterator adaptors. Because all iterators are lazy, however, we have to
-call one of the consuming adaptor methods in order to get results from calls
-to iterator adaptors. Listing 13-17 shows an example of calling the iterator
-adaptor method `map`, which takes a closure that `map` will call on each
-item in order to produce a new iterator in which each item from the vector has
+Other methods defined on the `Iterator` trait, known as *iterator adaptors*,
+allow us to change iterators into different kind of iterators. We can chain
+multiple calls to iterator adaptors to perform complex actions in a readable
+way. Because all iterators are lazy, however, we have to call one of the
+consuming adaptor methods in order to get results from calls to iterator
+adaptors.
+
+Listing 13-17 shows an example of calling the iterator adaptor method `map`
+which takes a closure to call on each item in order to produce a new iterator.
+The closure here creates a new iterator in which each item from the vector has
been incremented by 1. This code produces a warning, though:
Filename: src/main.rs
@@ -190,14 +195,15 @@ nothing unless consumed
The code in Listing 13-17 isn’t actually doing anything; the closure we’ve
specified never gets called. The warning reminds us why: iterator adaptors are
-lazy, and we probably meant to consume the iterator here.
+lazy, and we need to consume the iterator here.
-In order to fix this warning and consume the iterator to get a useful result,
-we’re going to use the `collect` method, which we saw briefly in Chapter 12.
-This method consumes the iterator and collects the resulting values into a
-data structure. In Listing 13-18, we’re going to collect the results of
-iterating over the iterator returned from the call to `map` into a vector that
-will contain each item from the original vector incremented by 1:
+To fix this and consume the iterator, we’re going to use the `collect` method,
+which we saw briefly in Chapter 12. This method consumes the iterator and
+collects the resulting values into a collection data type.
+
+In Listing 13-18, 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
+containing each item from the original vector incremented by 1:
Filename: src/main.rs
@@ -213,10 +219,10 @@ assert_eq!(v2, vec![2, 3, 4]);
iterator, then calling the `collect` method to consume the new iterator and
create a vector
-Because `map` takes a closure, we can specify any operation that we want to
-perform on each item that we iterate over. This is a great example of how using
-closures lets us customize some behavior while reusing the iteration behavior
-that the `Iterator` trait provides.
+Because `map` takes a closure, we can specify any operation we want to perform
+on each item. This is a great example of how closures let us customize some
+behavior while reusing the iteration behavior that the `Iterator` trait
+provides.
### Using Closures that Capture their Environment with Iterators
@@ -225,10 +231,11 @@ closures that capture their environment by using the `filter` iterator adapter.
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. Listing 13-19
-demonstrates using `filter` with a closure that captures the `shoe_size`
-variable from its environment in order to iterate over a collection of `Shoe`
-struct instances in order to return only shoes that are the specified size:
+`false`, the value won’t be included in the resulting iterator.
+
+In Listing 13-19 we use `filter` with a closure that captures the `shoe_size`
+variable from its environment, in order to iterate over a collection of `Shoe`
+struct instances. It will return only shoes that are the specified size:
Filename: src/lib.rs
@@ -270,14 +277,17 @@ that captures `shoe_size`
The `shoes_in_my_size` function takes ownership of a vector of shoes and a shoe
size as parameters. It returns a vector containing only shoes of the specified
-size. In the body of `shoes_in_my_size`, we call `into_iter` to create an
-iterator that takes ownership of the vector. Then we call `filter` to adapt
-that iterator into a new iterator that only contains elements for which the
-closure returns `true`. The closure we’ve specified captures the `shoe_size`
-parameter from the environment and uses the value to compare with each shoe’s
-size to only keep shoes that are of the size specified. Finally, calling
-`collect` gathers the values returned by the adapted iterator into a vector
-that the function returns.
+size.
+
+In the body of `shoes_in_my_size`, we call `into_iter` to create an iterator
+that takes ownership of the vector. Then we call `filter` to adapt that
+iterator into a new iterator that only contains elements for which the closure
+returns `true`.
+
+The closure captures the `shoe_size` parameter from the environment and
+compares the value with each shoe’s size, keeping only shoes of the size
+specified. Finally, calling `collect` gathers the values returned by the
+adapted iterator into a vector that’s returned by the function.
The test shows that when we call `shoes_in_my_size`, we only get back shoes
that have the same size as the value we specified.
@@ -285,20 +295,17 @@ that have the same size as the value we specified.
### Implementing the `Iterator` Trait to Create Our Own Iterators
We’ve shown that we can create an iterator by calling `iter`, `into_iter`, or
-`iter_mut` on a vector. We can also create iterators from the other collection
-types in the standard library, such as hash map. Additionally, we can implement
-the `Iterator` trait in order to create iterators that do anything we want.
-As previously mentioned, the only method we’re required to provide a definition
-for is the `next` method. Once we’ve done that, we can use all the other
-methods that have default implementations provided by the `Iterator` trait on
-our iterator!
+`iter_mut` on a vector. We can create iterators from the other collection types
+in the standard library, such as hash map. We can also create iterators that do
+anything we want by implementing the `Iterator` trait on our own types. As
+previously mentioned, the only method we’re required to provide a definition
+for is the `next` method. Once we’ve done that, we can use all other methods
+that have default implementations provided by the `Iterator` trait!
-
-
-The iterator we’re going to create is one that will only ever count from 1
-to 5. First, we’ll create a struct to hold on to some values, and then we’ll
-make this struct into an iterator by implementing the `Iterator` trait and use
-the values in that implementation.
+To demonstrate, let’s create an iterator that will only ever count from 1 to 5.
+First, we’ll create a struct to hold some values, and then we’ll make this
+struct into an iterator by implementing the `Iterator` trait and use the values
+in that implementation.
Listing 13-20 has the definition of the `Counter` struct and an associated
`new` function to create instances of `Counter`:
@@ -321,14 +328,14 @@ impl Counter {
function that creates instances of `Counter` with an initial value of 0 for
`count`
-The `Counter` struct has one field named `count`. This field holds a `u32`
-value that will keep track of where we are in the process of iterating from 1
-to 5. The `count` field is private since we want the implementation of
-`Counter` to manage its value. The `new` function enforces the behavior we want
-of always starting new instances with a value of 0 in the `count` field.
+The `Counter` struct has one field named `count`. This holds a `u32` value that
+will keep track of where we are in the process of iterating from 1 to 5. The
+`count` field is private since we want the implementation of `Counter` to
+manage its value. The `new` function enforces the behavior of always starting
+new instances with a value of 0 in the `count` field.
Next, we’re going to implement the `Iterator` trait for our `Counter` type by
-defining the body of the `next` method to specify what we want to happen when
+defining the body of the `next` method, to specify what we want to happen when
this iterator is used, as shown in Listing 13-21:
Filename: src/lib.rs
@@ -358,18 +365,19 @@ impl Iterator for Counter {
We set the associated `Item` type for our iterator to `u32`, meaning the
iterator will return `u32` values. Again, don’t worry about associated types
-yet, we’ll be covering them in Chapter 19. We want our iterator to add one to
-the current state, which is why we initialized `count` to 0: we want our
-iterator to return one first. If the value of `count` is less than six, `next`
-will return the current value wrapped in `Some`, but if `count` is six or
-higher, our iterator will return `None`.
+yet, we’ll be covering them in Chapter 19.
+
+We want our iterator to add one to the current state, so we initialized `count`
+to 0 so it would return one first. If the value of `count` is less than six,
+`next` will return the current value wrapped in `Some`, but if `count` is six
+or higher, our iterator will return `None`.
#### Using Our `Counter` Iterator’s `next` Method
Once we’ve implemented the `Iterator` trait, we have an iterator! Listing 13-22
-shows a test demonstrating that we can use the iterator functionality our
-`Counter` struct now has by calling the `next` method on it directly, just like
-we did with the iterator created from a vector in Listing 13-15:
+shows a test demonstrating that we can use the iterator functionality of our
+`Counter` struct by calling the `next` method on it directly, just like we did
+with the iterator created from a vector in Listing 13-15:
Filename: src/lib.rs
@@ -410,21 +418,19 @@ method implementation
This test creates a new `Counter` instance in the `counter` variable and then
calls `next` repeatedly, verifying that we have implemented the behavior we
-want this iterator to have of returning the values from 1 to 5.
+want this iterator to have: returning the values from 1 to 5.
#### Using Other `Iterator` Trait Methods on Our Iterator
Because we implemented the `Iterator` trait by defining the `next` method, we
-can now use any `Iterator` trait method’s default implementations that the
-standard library has defined, since they all use the `next` method’s
-functionality.
+can now use any `Iterator` trait method’s default implementations as defined in
+the standard library, since they all use the `next` method’s functionality.
-For example, if for some reason we wanted to take the values that an instance
-of `Counter` produces, pair those values with values produced by another
-`Counter` instance after skipping the first value that instance produces,
-multiply each pair together, keep only those results that are divisible by
-three, and add all the resulting values together, we could do so as shown in
-the test in Listing 13-23:
+For example, if for some reason we wanted to take the values produced by an
+instance of `Counter`, pair them with values produced by another `Counter`
+instance after skipping the first value, multiply each pair together, keep only
+those results that are divisible by three, and add all the resulting values
+together, we could do so as shown in the test in Listing 13-23:
Filename: src/lib.rs
@@ -473,6 +479,6 @@ Note that `zip` produces only four pairs; the theoretical fifth pair `(5,
None)` is never produced because `zip` returns `None` when either of its input
iterators return `None`.
-All of these method calls are possible because we implemented the `Iterator`
-trait by specifying how the `next` method works and the standard library
-provides default implementations for other methods that call `next`.
+All of these method calls are possible because we specified how the `next`
+method works, and the standard library provides default implementations for
+other methods that call `next`.
diff --git a/second-edition/src/ch13-03-improving-our-io-project.md b/second-edition/src/ch13-03-improving-our-io-project.md
index 88493668d..b2dea45e1 100644
--- a/second-edition/src/ch13-03-improving-our-io-project.md
+++ b/second-edition/src/ch13-03-improving-our-io-project.md
@@ -1,6 +1,6 @@
## Improving our I/O Project
-We can improve our implementation of the I/O project in Chapter 12 by using
+With this new knowledge, we can improve the I/O project in Chapter 12 by using
iterators to make places in the code clearer and more concise. Let’s take a
look at how iterators can improve our implementation of both the `Config::new`
function and the `search` function.
@@ -9,7 +9,7 @@ function and the `search` function.
In Listing 12-6, we added code that took a slice of `String` values and created
an instance of the `Config` struct by indexing into the slice and cloning the
-values so that the `Config` struct could own those values. We’ve reproduced the
+values, allowing the `Config` struct to own those values. We’ve reproduced the
implementation of the `Config::new` function as it was at the end of Chapter 12
in Listing 13-24:
@@ -38,27 +38,29 @@ from the end of Chapter 12
At the time, we said not to worry about the inefficient `clone` calls here
because we would remove them in the future. Well, that time is now!
-The reason we needed `clone` here in the first place is that we have a slice
-with `String` elements in the parameter `args`, but the `new` function does not
-own `args`. In order to be able to return ownership of a `Config` instance, we
-need to clone the values that we put in the `query` and `filename` fields of
-`Config`, so that the `Config` instance can own its values.
+We needed `clone` here because we have a slice with `String` elements in the
+parameter `args`, but the `new` function doesn’t own `args`. In order to be
+able to return ownership of a `Config` instance, we had to clone the values
+from the `query` and `filename` fields of `Config`, so that the `Config`
+instance can own its values.
With our new knowledge about iterators, we can change the `new` function to
take ownership of an iterator as its argument instead of borrowing a slice.
-We’ll use the iterator functionality instead of the code we had that checks the
-length of the slice and indexes into specific locations. This will clear up
-what the `Config::new` function is doing since the iterator will take care of
-accessing the values.
+We’ll use the iterator functionality instead of the code that checks the length
+of the slice and indexes into specific locations. This will clear up what the
+`Config::new` function is doing since the iterator will take care of accessing
+the values.
-Once `Config::new` taking ownership of the iterator and not using indexing
+Once `Config::new` takes ownership of the iterator and stops using indexing
operations that borrow, we can move the `String` values from the iterator into
`Config` rather than calling `clone` and making a new allocation.
#### Using the Iterator Returned by `env::args` Directly
-In your I/O project’s *src/main.rs*, let’s change the start of the `main`
-function from this code that we had at the end of Chapter 12:
+Open your I/O project’s *src/main.rs*, and we’ll change the start of the `main`
+function that we had at the end of Chapter 12:
+
+Filename: src/main.rs
```rust,ignore
fn main() {
@@ -120,8 +122,8 @@ type `std::env::Args` instead of `&[String]`.
Next, we’ll fix the body of `Config::new`. The standard library documentation
also mentions that `std::env::Args` implements the `Iterator` trait, so we know
-we can call the `next` method on it! Listing 13-27 has updated the code
-from Listing 12-23 to use the `next` method:
+we can call the `next` method on it! Listing 13-27 has updated the code from
+Listing 12-23 to use the `next` method:
Filename: src/lib.rs
@@ -136,7 +138,7 @@ from Listing 12-23 to use the `next` method:
#
impl Config {
pub fn new(mut args: std::env::Args) -> Result {
- args.next();
+ args.next();
let query = match args.next() {
Some(arg) => arg,
@@ -150,9 +152,7 @@ impl Config {
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
- Ok(Config {
- query, filename, case_sensitive
- })
+ Ok(Config { query, filename, case_sensitive })
}
}
```
@@ -193,8 +193,8 @@ pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
Listing 13-28: The implementation of the `search`
function from Chapter 12
-We can write this code in a much shorter way by using iterator adaptor methods
-instead. This also lets us avoid having a mutable intermediate `results`
+We can write this code in a much more concise way using iterator adaptor
+methods. This also lets us avoid having a mutable intermediate `results`
vector. The functional programming style prefers to minimize the amount of
mutable state to make code clearer. Removing the mutable state might make it
easier for us to make a future enhancement to make searching happen in
@@ -215,18 +215,18 @@ pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
implementation of the `search` function
Recall that the purpose of the `search` function is to return all lines in
-`contents` that contain the `query`. Similarly to the `filter` example in
-Listing 13-19, we can use the `filter` adaptor to keep only the lines that
+`contents` that contain the `query`. Similar to the `filter` example in Listing
+13-19, we can use the `filter` adaptor to keep only the lines that
`line.contains(query)` returns true for. We then collect the matching lines up
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.
-The next logical question is which style you should choose in your own code:
-the original implementation in Listing 13-28, or the version using iterators in
-Listing 13-29. Most Rust programmers prefer to use the iterator style. It’s a
-bit tougher to get the hang of at first, but once you get a feel for the
-various iterator adaptors and what they do, iterators can be easier to
+The next logical question is which style you should choose in your own code and
+why: the original implementation in Listing 13-28, or the version using
+iterators in Listing 13-29. Most Rust programmers prefer to use the iterator
+style. It’s a bit tougher to get the hang of at first, but once you get a feel
+for the various iterator adaptors and what they do, iterators can be easier to
understand. Instead of fiddling with the various bits of looping and building
new vectors, the code focuses on the high-level objective of the loop. This
abstracts away some of the commonplace code so that it’s easier to see the
diff --git a/second-edition/src/ch13-04-performance.md b/second-edition/src/ch13-04-performance.md
index b3f56eb00..c7a1efe01 100644
--- a/second-edition/src/ch13-04-performance.md
+++ b/second-edition/src/ch13-04-performance.md
@@ -17,14 +17,16 @@ test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
The iterator version ended up slightly faster! We’re not going to go through
the benchmark code here, as the point is not to prove that they’re exactly
equivalent, but to get a general sense of how these two implementations compare
-performance-wise. For a more comprehensive benchmark, you’d want to check
-various texts of various sizes, different words, words of different lengths,
-and all kinds of other variations. The point is this: iterators, while a
-high-level abstraction, get compiled down to roughly the same code as if you’d
-written the lower-level code yourself. Iterators are one of Rust’s *zero-cost
-abstractions*, by which we mean using the abstraction imposes no additional
-runtime overhead in the same way that Bjarne Stroustrup, the original designer
-and implementor of C++, defines *zero-overhead*:
+performance-wise.
+
+For a more comprehensive benchmark, you’d want to check various texts of
+various sizes, different words, words of different lengths, and all kinds of
+other variations. The point is this: iterators, while a high-level abstraction,
+get compiled down to roughly the same code as if you’d written the lower-level
+code yourself. Iterators are one of Rust’s *zero-cost* *abstractions*, by which
+we mean using the abstraction imposes no additional runtime overhead, in the
+same way that Bjarne Stroustrup, the original designer and implementor of C++,
+defines *zero-overhead*:
> In general, C++ implementations obey the zero-overhead principle: What you
> don’t use, you don’t pay for. And further: What you do use, you couldn’t hand
@@ -70,7 +72,7 @@ consuming the value. What assembly code would this Rust code compile to? Well,
as of this writing, it compiles down to the same assembly you’d write by hand.
There’s no loop at all corresponding to the iteration over the values in
`coefficients`: Rust knows that there are twelve iterations, so it “unrolls”
-the loop. Unrolling is an optimization that removes the overhead of the loop
+the loop. *Unrolling* is an optimization that removes the overhead of the loop
controlling code and instead generates repetitive code for each iteration of
the loop.