Propagate ch18 edits to src

This commit is contained in:
Carol (Nichols || Goulding) 2022-05-21 20:04:31 -04:00
parent c265d2abdf
commit 0c2d41e2af
No known key found for this signature in database
GPG Key ID: E907EE5A736F87D4
4 changed files with 123 additions and 119 deletions

View File

@ -1,6 +1,6 @@
# Patterns and Matching
Patterns are a special syntax in Rust for matching against the structure of
*Patterns* are a special syntax in Rust for matching against the structure of
types, both complex and simple. Using patterns in conjunction with `match`
expressions and other constructs gives you more control over a programs
control flow. A pattern consists of some combination of the following:

View File

@ -19,16 +19,29 @@ match VALUE {
}
```
For example, here's the `match` expression from Listing 6-5 that matches on an
`Option<i32>` value in the variable `x`:
```rust,ignore
match x {
None => None,
Some(i) => Some(i + 1),
}
```
The patterns in this `match` expression are the `None` and `Some(i)` on the
left of each arrow.
One requirement for `match` expressions is that they need to be *exhaustive* in
the sense that all possibilities for the value in the `match` expression must
be accounted for. One way to ensure youve covered every possibility is to have
a catchall pattern for the last arm: for example, a variable name matching any
value can never fail and thus covers every remaining case.
A particular pattern `_` will match anything, but it never binds to a variable,
so its often used in the last match arm. The `_` pattern can be useful when
you want to ignore any value not specified, for example. Well cover the `_`
pattern in more detail in the [“Ignoring Values in a
The particular pattern `_` will match anything, but it never binds to a
variable, so its often used in the last match arm. The `_` pattern can be
useful when you want to ignore any value not specified, for example. Well
cover the `_` pattern in more detail in the [“Ignoring Values in a
Pattern”][ignoring-values-in-a-pattern]<!-- ignore --> section later in this
chapter.
@ -42,11 +55,11 @@ the pattern in the `if let` doesnt match.
Listing 18-1 shows that its also possible to mix and match `if let`, `else
if`, and `else if let` expressions. Doing so gives us more flexibility than a
`match` expression in which we can express only one value to compare with the
patterns. Also, the conditions in a series of `if let`, `else if`, `else if
let` arms arent required to relate to each other.
patterns. Also, Rust doesn't require that the conditions in a series of `if
let`, `else if`, `else if let` arms relate to each other.
The code in Listing 18-1 shows a series of checks for several conditions that
decide what the background color should be. For this example, weve created
The code in Listing 18-1 determines what color to make your background based on
a series of checks for several conditions. For this example, weve created
variables with hardcoded values that a real program might receive from user
input.
@ -59,11 +72,12 @@ input.
<span class="caption">Listing 18-1: Mixing `if let`, `else if`, `else if let`,
and `else`</span>
If the user specifies a favorite color, that color is the background color. If
today is Tuesday, the background color is green. If the user specifies
their age as a string and we can parse it as a number successfully, the color
is either purple or orange depending on the value of the number. If none of
these conditions apply, the background color is blue.
If the user specifies a favorite color, that color is used as the background.
If no favorite color is specified and today is Tuesday, the background color is
green. Otherwise, if the user specifies their age as a string and we can parse
it as a number successfully, the color is either purple or orange depending on
the value of the number. If none of these conditions apply, the background
color is blue.
This conditional structure lets us support complex requirements. With the
hardcoded values we have here, this example will print `Using purple as the
@ -78,16 +92,16 @@ shadowed `age` we want to compare to 30 isnt valid until the new scope starts
with the curly bracket.
The downside of using `if let` expressions is that the compiler doesnt check
exhaustiveness, whereas with `match` expressions it does. If we omitted the
for exhaustiveness, whereas with `match` expressions it does. If we omitted the
last `else` block and therefore missed handling some cases, the compiler would
not alert us to the possible logic bug.
### `while let` Conditional Loops
Similar in construction to `if let`, the `while let` conditional loop allows a
`while` loop to run for as long as a pattern continues to match. The example in
Listing 18-2 shows a `while let` loop that uses a vector as a stack and prints
the values in the vector in the opposite order in which they were pushed.
`while` loop to run for as long as a pattern continues to match. In Listing
18-2 we code a `while let` loop that uses a vector as a stack and prints the
values in the vector in the opposite order in which they were pushed.
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-02/src/main.rs:here}}
@ -104,13 +118,10 @@ use `while let` to pop every element off our stack.
### `for` Loops
In Chapter 3, we mentioned that the `for` loop is the most common loop
construction in Rust code, but we havent yet discussed the pattern that `for`
takes. In a `for` loop, the pattern is the value that directly follows the
keyword `for`, so in `for x in y` the `x` is the pattern.
Listing 18-3 demonstrates how to use a pattern in a `for` loop to destructure,
or break apart, a tuple as part of the `for` loop.
In a `for` loop, the value that directly follows the keyword `for` is a
pattern. For example, in `for x in y` the `x` is the pattern. Listing 18-3
demonstrates how to use a pattern in a `for` loop to destructure, or break
apart, a tuple as part of the `for` loop.
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-03/src/main.rs:here}}
@ -125,11 +136,11 @@ The code in Listing 18-3 will print the following:
{{#include ../listings/ch18-patterns-and-matching/listing-18-03/output.txt}}
```
We use the `enumerate` method to adapt an iterator to produce a value and that
values index in the iterator, placed into a tuple. The first value produced is
the tuple `(0, 'a')`. When this value is matched to the pattern `(index,
value)`, `index` will be `0` and `value` will be `'a'`, printing the first line
of the output.
We adapt an iterator using the `enumerate` method so it produces a value and
the index for that value, placed into a tuple. The first value produced is the
tuple `(0, 'a')`. When this value is matched to the pattern `(index, value)`,
`index` will be `0` and `value` will be `'a'`, printing the first line of the
output.
### `let` Statements
@ -142,9 +153,9 @@ variable assignment with `let`:
let x = 5;
```
Throughout this book, weve used `let` like this hundreds of times, and
although you might not have realized it, you were using patterns! More
formally, a `let` statement looks like this:
Every time you've used a `let` statement like this you've been using patterns,
although you might not have realized it! More formally, a `let` statement looks
like this:
```text
let PATTERN = EXPRESSION;
@ -190,8 +201,8 @@ Attempting to compile this code results in this type error:
{{#include ../listings/ch18-patterns-and-matching/listing-18-05/output.txt}}
```
If we wanted to ignore one or more of the values in the tuple, we could use `_`
or `..`, as youll see in the [“Ignoring Values in a
To fix the error, we could ignore one or more of the values in the tuple using
`_` or `..`, as youll see in the [“Ignoring Values in a
Pattern”][ignoring-values-in-a-pattern]<!-- ignore --> section. If the problem
is that we have too many variables in the pattern, the solution is to make the
types match by removing variables so the number of variables equals the number

View File

@ -47,11 +47,11 @@ use a refutable pattern where an irrefutable pattern is required:
Because we didnt cover (and couldnt cover!) every valid value with the
pattern `Some(x)`, Rust rightfully produces a compiler error.
To fix the problem where we have a refutable pattern where an irrefutable
pattern is needed, we can change the code that uses the pattern: instead of
using `let`, we can use `if let`. Then if the pattern doesnt match, the code
will just skip the code in the curly brackets, giving it a way to continue
validly. Listing 18-9 shows how to fix the code in Listing 18-8.
If we have a refutable pattern where an irrefutable pattern is needed, we can
fix it by changing the code that uses the pattern: instead of using `let`, we
can use `if let`. Then if the pattern doesnt match, the code will just skip
the code in the curly brackets, giving it a way to continue validly. Listing
18-9 shows how to fix the code in Listing 18-8.
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-09/src/main.rs:here}}

View File

@ -1,8 +1,7 @@
## Pattern Syntax
Throughout the book, youve seen examples of many kinds of patterns. In this
section, we gather all the syntax valid in patterns and discuss why you might
want to use each one.
In this section, we gather all the syntax valid in patterns and discuss why and
when you might want to use each one.
### Matching Literals
@ -70,10 +69,10 @@ ignore --> section.
### Multiple Patterns
In `match` expressions, you can match multiple patterns using the `|` syntax,
which means *or*. For example, the following code matches the value of `x`
against the match arms, the first of which has an *or* option, meaning if the
value of `x` matches either of the values in that arm, that arms code will
run:
which is the pattern *or* operator. For example, in the following code we match
the value of `x` against the match arms, the first of which has an *or* option,
meaning if the value of `x` matches either of the values in that arm, that
arms code will run:
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/no-listing-02-multiple-patterns/src/main.rs:here}}
@ -84,22 +83,22 @@ This code prints `one or two`.
### Matching Ranges of Values with `..=`
The `..=` syntax allows us to match to an inclusive range of values. In the
following code, when a pattern matches any of the values within the range, that
arm will execute:
following code, when a pattern matches any of the values within the given
range, that arm will execute:
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/no-listing-03-ranges/src/main.rs:here}}
```
If `x` is 1, 2, 3, 4, or 5, the first arm will match. This syntax is more
convenient than using the `|` operator to express the same idea; instead of
`1..=5`, we would have to specify `1 | 2 | 3 | 4 | 5` if we used `|`.
convenient for multiple match values than using the `|` operator to express the
same idea; if we were to use `|` we would have to specify `1 | 2 | 3 | 4 | 5`.
Specifying a range is much shorter, especially if we want to match, say, any
number between 1 and 1,000!
Ranges are only allowed with numeric values or `char` values, because the
compiler checks that the range isnt empty at compile time. The only types for
which Rust can tell if a range is empty or not are `char` and numeric values.
The compiler checks that the range isnt empty at compile time, and because the
only types for which Rust can tell if a range is empty or not are `char` and
numeric values, ranges are only allowed with numeric or `char` values.
Here is an example using ranges of `char` values:
@ -131,17 +130,15 @@ separate variables</span>
This code creates the variables `a` and `b` that match the values of the `x`
and `y` fields of the `p` struct. This example shows that the names of the
variables in the pattern dont have to match the field names of the struct. But
its common to want the variable names to match the field names to make it
easier to remember which variables came from which fields.
Because having variable names match the fields is common and because writing
`let Point { x: x, y: y } = p;` contains a lot of duplication, there is a
shorthand for patterns that match struct fields: you only need to list the name
of the struct field, and the variables created from the pattern will have the
same names. Listing 18-13 shows code that behaves in the same way as the code
in Listing 18-12, but the variables created in the `let` pattern are `x` and
`y` instead of `a` and `b`.
variables in the pattern dont have to match the field names of the struct.
However, its common to match the variable names to the field names to make it
easier to remember which variables came from which fields. Because of this
common usage, and because writing `let Point { x: x, y: y } = p;` contains a
lot of duplication, Rust has a shorthand for patterns that match struct fields:
you only need to list the name of the struct field, and the variables created
from the pattern will have the same names. Listing 18-13 behaves in the same
way as the code in Listing 18-12, but the variables created in the `let`
pattern are `x` and `y` instead of `a` and `b`.
<span class="filename">Filename: src/main.rs</span>
@ -161,9 +158,9 @@ rather than creating variables for all the fields. Doing so allows us to test
some of the fields for particular values while creating variables to
destructure the other fields.
Listing 18-14 shows a `match` expression that separates `Point` values into
three cases: points that lie directly on the `x` axis (which is true when `y =
0`), on the `y` axis (`x = 0`), or neither.
In Listing 18-14, we have a `match` expression that separates `Point` values
into three cases: points that lie directly on the `x` axis (which is true when
`y = 0`), on the `y` axis (`x = 0`), or neither.
<span class="filename">Filename: src/main.rs</span>
@ -188,10 +185,9 @@ containing a 0, so this code will print `On the y axis at 7`.
#### Destructuring Enums
Weve destructured enums earlier in this book, for example, when we
destructured `Option<i32>` in Listing 6-5 in Chapter 6. One detail we havent
mentioned explicitly is that the pattern to destructure an enum should
correspond to the way the data stored within the enum is defined. As an
We've destructured enums in this book (for example, Listing 6-5 in Chapter 6),
but havent yet explicitly discussed that the pattern to destructure an enum
corresponds to the way the data stored within the enum is defined. As an
example, in Listing 18-15 we use the `Message` enum from Listing 6-2 and write
a `match` with patterns that will destructure each inner value.
@ -225,11 +221,10 @@ matching.
#### Destructuring Nested Structs and Enums
Until now, all our examples have been matching structs or enums that were one
level deep. Matching can work on nested items too!
For example, we can refactor the code in Listing 18-15 to support RGB and HSV
colors in the `ChangeColor` message, as shown in Listing 18-16.
So far, our examples have all been matching structs or enums one level deep,
but matching can work on nested items too! For example, we can refactor the
code in Listing 18-15 to support RGB and HSV colors in the `ChangeColor`
message, as shown in Listing 18-16.
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-16/src/main.rs}}
@ -241,8 +236,8 @@ The pattern of the first arm in the `match` expression matches a
`Message::ChangeColor` enum variant that contains a `Color::Rgb` variant; then
the pattern binds to the three inner `i32` values. The pattern of the second
arm also matches a `Message::ChangeColor` enum variant, but the inner enum
matches the `Color::Hsv` variant instead. We can specify these complex
conditions in one `match` expression, even though two enums are involved.
matches `Color::Hsv` instead. We can specify these complex conditions in one
`match` expression, even though two enums are involved.
#### Destructuring Structs and Tuples
@ -272,10 +267,10 @@ parts of a value. Lets explore how and why to use each of these patterns.
#### Ignoring an Entire Value with `_`
Weve used the underscore (`_`) as a wildcard pattern that will match any value
but not bind to the value. Although the underscore `_` pattern is especially
useful as the last arm in a `match` expression, we can use it in any pattern,
including function parameters, as shown in Listing 18-17.
Weve used the underscore as a wildcard pattern that will match any value but
not bind to the value. This is especially useful as the last arm in a `match`
expression, but we can also use it in any pattern, including function
parameters, as shown in Listing 18-17.
<span class="filename">Filename: src/main.rs</span>
@ -285,16 +280,16 @@ including function parameters, as shown in Listing 18-17.
<span class="caption">Listing 18-17: Using `_` in a function signature</span>
This code will completely ignore the value passed as the first argument, `3`,
This code will completely ignore the value `3` passed as the first argument,
and will print `This code only uses the y parameter: 4`.
In most cases when you no longer need a particular function parameter, you
would change the signature so it doesnt include the unused parameter. Ignoring
a function parameter can be especially useful in some cases, for example, when
implementing a trait when you need a certain type signature but the function
body in your implementation doesnt need one of the parameters. The compiler
will then not warn about unused function parameters, as it would if you used a
name instead.
a function parameter can be especially useful in cases when, for example,
you're implementing a trait when you need a certain type signature but the
function body in your implementation doesnt need one of the parameters. You
then avoid getting a compiler warning about unused function parameters, as you
would if you used a name instead.
#### Ignoring Parts of a Value with a Nested `_`
@ -317,7 +312,7 @@ This code will print `Can't overwrite an existing customized value` and then
`setting is Some(5)`. In the first match arm, we dont need to match on or use
the values inside either `Some` variant, but we do need to test for the case
when `setting_value` and `new_setting_value` are the `Some` variant. In that
case, we print why were not changing `setting_value`, and it doesnt get
case, we print the reason for not changing `setting_value`, and it doesnt get
changed.
In all other cases (if either `setting_value` or `new_setting_value` are
@ -340,12 +335,12 @@ ignored.
#### Ignoring an Unused Variable by Starting Its Name with `_`
If you create a variable but dont use it anywhere, Rust will usually issue a
warning because that could be a bug. But sometimes its useful to create a
variable you wont use yet, such as when youre prototyping or just starting a
project. In this situation, you can tell Rust not to warn you about the unused
variable by starting the name of the variable with an underscore. In Listing
18-20, we create two unused variables, but when we compile this code, we should
only get a warning about one of them.
warning because an unused variable could be a bug. However, sometimes its
useful to be able to create a variable you wont use yet, such as when youre
prototyping or just starting a project. In this situation, you can tell Rust
not to warn you about the unused variable by starting the name of the variable
with an underscore. In Listing 18-20, we create two unused variables, but when
we compile this code, we should only get a warning about one of them.
<span class="filename">Filename: src/main.rs</span>
@ -357,7 +352,7 @@ only get a warning about one of them.
underscore to avoid getting unused variable warnings</span>
Here we get a warning about not using the variable `y`, but we dont get a
warning about not using the variable preceded by the underscore.
warning about not using `_x`.
Note that there is a subtle difference between using only `_` and using a name
that starts with an underscore. The syntax `_x` still binds the value to the
@ -387,7 +382,7 @@ This code works just fine because we never bind `s` to anything; it isnt move
#### Ignoring Remaining Parts of a Value with `..`
With values that have many parts, we can use the `..` syntax to use only a few
With values that have many parts, we can use the `..` syntax to use specific
parts and ignore the rest, avoiding the need to list underscores for each
ignored value. The `..` pattern ignores any parts of a value that we havent
explicitly matched in the rest of the pattern. In Listing 18-23, we have a
@ -452,10 +447,9 @@ compiler error because using `..` in two places like this is ambiguous.
### Extra Conditionals with Match Guards
A *match guard* is an additional `if` condition specified after the pattern in
a `match` arm that must also match, along with the pattern matching, for that
arm to be chosen. Match guards are useful for expressing more complex ideas
than a pattern alone allows.
A *match guard* is an additional `if` condition, specified after the pattern in
a `match` arm, that must also match for that arm to be chosen. Match guards are
useful for expressing more complex ideas than a pattern alone allows.
The condition can use variables created in the pattern. Listing 18-26 shows a
`match` where the first arm has the pattern `Some(x)` and also has a match
@ -483,7 +477,7 @@ this additional expressiveness is that the compiler doesn't try to check for
exhaustiveness when match guard expressions are involved.
In Listing 18-11, we mentioned that we could use match guards to solve our
pattern-shadowing problem. Recall that a new variable was created inside the
pattern-shadowing problem. Recall that we created a new variable inside the
pattern in the `match` expression instead of using the variable outside the
`match`. That new variable meant we couldnt test against the value of the
outer variable. Listing 18-27 shows how we can use a match guard to fix this
@ -512,10 +506,10 @@ we can look for a value that has the same value as the outer `y` by comparing
You can also use the *or* operator `|` in a match guard to specify multiple
patterns; the match guard condition will apply to all the patterns. Listing
18-28 shows the precedence of combining a match guard with a pattern that uses
`|`. The important part of this example is that the `if y` match guard applies
to `4`, `5`, *and* `6`, even though it might look like `if y` only applies to
`6`.
18-28 shows the precedence when combining a pattern that uses `|` with a match
guard. The important part of this example is that the `if y` match guard
applies to `4`, `5`, *and* `6`, even though it might look like `if y` only
applies to `6`.
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-28/src/main.rs:here}}
@ -550,13 +544,12 @@ were applied only to the final value in the list of values specified using the
### `@` Bindings
The *at* operator (`@`) lets us create a variable that holds a value at the
same time were testing that value to see whether it matches a pattern. Listing
18-29 shows an example where we want to test that a `Message::Hello` `id` field
is within the range `3..=7`. But we also want to bind the value to the variable
`id_variable` so we can use it in the code associated with the arm. We could
name this variable `id`, the same as the field, but for this example well use
a different name.
The *at* operator `@` lets us create a variable that holds a value at the same
time as were testing that value for a pattern match. In Listing 18-29, we want
to test that a `Message::Hello` `id` field is within the range `3..=7`. We also
want to bind the value to the variable `id_variable` so we can use it in the
code associated with the arm. We could name this variable `id`, the same as the
field, but for this example well use a different name.
```rust
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-29/src/main.rs:here}}
@ -586,11 +579,11 @@ Using `@` lets us test a value and save it in a variable within one pattern.
## Summary
Rusts patterns are very useful in that they help distinguish between different
kinds of data. When used in `match` expressions, Rust ensures your patterns
cover every possible value, or your program wont compile. Patterns in `let`
statements and function parameters make those constructs more useful, enabling
the destructuring of values into smaller parts at the same time as assigning to
Rusts patterns are very useful in distinguishing between different kinds of
data. When used in `match` expressions, Rust ensures your patterns cover every
possible value, or your program wont compile. Patterns in `let` statements and
function parameters make those constructs more useful, enabling the
destructuring of values into smaller parts at the same time as assigning to
variables. We can create simple or complex patterns to suit our needs.
Next, for the penultimate chapter of the book, well look at some advanced