diff options
Diffstat (limited to '')
-rw-r--r-- | src/doc/book/nostarch/chapter18.md | 1282 |
1 files changed, 1282 insertions, 0 deletions
diff --git a/src/doc/book/nostarch/chapter18.md b/src/doc/book/nostarch/chapter18.md new file mode 100644 index 000000000..0b104dcf6 --- /dev/null +++ b/src/doc/book/nostarch/chapter18.md @@ -0,0 +1,1282 @@ +<!-- DO NOT EDIT THIS FILE. + +This file is periodically generated from the content in the `/src/` +directory, so all fixes need to be made in `/src/`. +--> + +[TOC] + +# Patterns and Matching + +*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 program’s +control flow. A pattern consists of some combination of the following: + +* Literals +* Destructured arrays, enums, structs, or tuples +* Variables +* Wildcards +* Placeholders + +Some example patterns include `x`, `(a, 3)`, and `Some(Color::Red)`. In the +contexts in which patterns are valid, these components describe the shape of +data. Our program then matches values against the patterns to determine whether +it has the correct shape of data to continue running a particular piece of code. +<!-- is there some generic pattern we can show as an example, early on, or +is it too dependent on where the pattern is used? /LC --> +<!-- Yeah, if a pattern is out of context, it doesn't look special. Like `3` +can be a pattern. /Carol --> +<!-- We could mention something like, "If you've written a little Rust, you've +already used patterns without knowing it. For example `let x = 3`, the `x` is +a pattern." Though looks like we use this later. + +Or, we could say, "Some example patterns include: `x`, `(a, b)`, and `Color::Red` +/JT --> +<!-- Ok, I've tried rewording this paragraph to include some examples, I do +think it's important to emphasize that these are only patterns in the contexts +patterns may exist, because without the context, there's no way to distinguish +patterns from regular values /Carol --> + +To use a pattern, we compare it to some value. If the pattern matches the +value, we use the value parts in our code. Recall the `match` expressions in +Chapter 6 that used patterns, such as the coin-sorting machine example. If the +value fits the shape of the pattern, we can use the named pieces. If it +doesn’t, the code associated with the pattern won’t run. + +This chapter is a reference on all things related to patterns. We’ll cover the +valid places to use patterns, the difference between refutable and irrefutable +patterns, and the different kinds of pattern syntax that you might see. By the +end of the chapter, you’ll know how to use patterns to express many concepts in +a clear way. + +## All the Places Patterns Can Be Used + +Patterns pop up in a number of places in Rust, and you’ve been using them a lot +without realizing it! This section discusses all the places where patterns are +valid. + +### `match` Arms + +As discussed in Chapter 6, we use patterns in the arms of `match` expressions. +Formally, `match` expressions are defined as the keyword `match`, a value to +match on, and one or more match arms that consist of a pattern and an +expression to run if the value matches that arm’s pattern, like this: + +``` +match VALUE { + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, +} +``` + +For example, here's the `match` expression from Listing 6-5 that matches on an +`Option<i32>` value in the variable `x`: + +``` +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 you’ve 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. + +The particular pattern `_` will match anything, but it never binds to a +variable, so it’s often used in the last match arm. The `_` pattern can be +useful when you want to ignore any value not specified, for example. We’ll +cover the `_` pattern in more detail in the “Ignoring Values in a Pattern” +section later in this chapter. + +### Conditional `if let` Expressions + +In Chapter 6 we discussed how to use `if let` expressions mainly as a shorter +way to write the equivalent of a `match` that only matches one case. +Optionally, `if let` can have a corresponding `else` containing code to run if +the pattern in the `if let` doesn’t match. + +Listing 18-1 shows that it’s 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, 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 determines what color to make your background based on +a series of checks for several conditions. For this example, we’ve created +variables with hardcoded values that a real program might receive from user +input. + +Filename: src/main.rs + +``` +fn main() { + let favorite_color: Option<&str> = None; + let is_tuesday = false; + let age: Result<u8, _> = "34".parse(); + + [1] if let Some(color) = favorite_color { + [2] println!("Using your favorite color, {color}, as the background"); + [3] } else if is_tuesday { + [4] println!("Tuesday is green day!"); + [5]} else if let Ok(age) = age { + [6] if age > 30 { + [7] println!("Using purple as the background color"); + } else { + [8] println!("Using orange as the background color"); + } + [9] } else { + [10] println!("Using blue as the background color"); + } +} +``` + +Listing 18-1: Mixing `if let`, `else if`, `else if let`, and `else` + +If the user specifies a favorite color [1], that color is used as the +background [2]. If no favorite color is specified and today is Tuesday [3], the +background color is green [4]. Otherwise, if the user specifies their age as a +string and we can parse it as a number successfully [5], the color is either +purple [7] or orange [8] depending on the value of the number [6]. If none of +these conditions apply [9], 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 +background color`. + +You can see that `if let` can also introduce shadowed variables in the same way +that `match` arms can: the line `if let Ok(age) = age` [5] introduces a new +shadowed `age` variable that contains the value inside the `Ok` variant. This +means we need to place the `if age > 30` condition [6] within that block: we +can’t combine these two conditions into `if let Ok(age) = age && age > 30`. The +shadowed `age` we want to compare to 30 isn’t valid until the new scope starts +with the curly bracket. + +<!-- Have we given them an intuition yet for why this is? I may be forgetting +something from the earlier chapters, but I wonder if we should reiterate that +when a pattern matches, the variable that gets bound is only valid for the +expression or block that follows the match. /JT --> +<!-- I don't really see a difference between saying "The shadowed `age` we want +to compare to 30 isn't valid until the new scope starts with the curly bracket" +and "when a pattern matches, the variable that gets bound is only valid for the +expression or block that follows the match"? To me, it sounds like this would +be saying the same thing twice, so I'm not going to change anything here. +/Carol --> + +The downside of using `if let` expressions is that the compiler doesn’t check +for exhaustiveness, whereas with `match` expressions it does. If we omitted the +last `else` block [9] 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. 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. + +``` +let mut stack = Vec::new(); + +stack.push(1); +stack.push(2); +stack.push(3); + +while let Some(top) = stack.pop() { + println!("{}", top); +} +``` + +Listing 18-2: Using a `while let` loop to print values for as long as +`stack.pop()` returns `Some` + +This example prints 3, 2, and then 1. The `pop` method takes the last element +out of the vector and returns `Some(value)`. If the vector is empty, `pop` +returns `None`. The `while` loop continues running the code in its block as +long as `pop` returns `Some`. When `pop` returns `None`, the loop stops. We can +use `while let` to pop every element off our stack. + +### `for` Loops + +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. + +``` +let v = vec!['a', 'b', 'c']; + +for (index, value) in v.iter().enumerate() { + println!("{} is at index {}", value, index); +} +``` + +Listing 18-3: Using a pattern in a `for` loop to destructure a tuple + +The code in Listing 18-3 will print the following: + +``` +a is at index 0 +b is at index 1 +c is at index 2 +``` + +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 + +Prior to this chapter, we had only explicitly discussed using patterns with +`match` and `if let`, but in fact, we’ve used patterns in other places as well, +including in `let` statements. For example, consider this straightforward +variable assignment with `let`: + +``` +let x = 5; +``` + +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: + +``` +let PATTERN = EXPRESSION; +``` + +In statements like `let x = 5;` with a variable name in the `PATTERN` slot, the +variable name is just a particularly simple form of a pattern. Rust compares +the expression against the pattern and assigns any names it finds. So in the +`let x = 5;` example, `x` is a pattern that means “bind what matches here to +the variable `x`.” Because the name `x` is the whole pattern, this pattern +effectively means “bind everything to the variable `x`, whatever the value is.” + +To see the pattern matching aspect of `let` more clearly, consider Listing +18-4, which uses a pattern with `let` to destructure a tuple. + +``` +let (x, y, z) = (1, 2, 3); +``` + +Listing 18-4: Using a pattern to destructure a tuple and create three variables +at once + +Here, we match a tuple against a pattern. Rust compares the value `(1, 2, 3)` +to the pattern `(x, y, z)` and sees that the value matches the pattern, in that +it sees that the number of elements is the same in both, so Rust +binds `1` to `x`, `2` to `y`, and `3` to `z`. You can think of this tuple +pattern as nesting three individual variable patterns inside it. + +If the number of elements in the pattern doesn’t match the number of elements +in the tuple, the overall type won’t match and we’ll get a compiler error. For +example, Listing 18-5 shows an attempt to destructure a tuple with three +elements into two variables, which won’t work. + +``` +let (x, y) = (1, 2, 3); +``` + +Listing 18-5: Incorrectly constructing a pattern whose variables don’t match +the number of elements in the tuple + +Attempting to compile this code results in this type error: + +``` +error[E0308]: mismatched types + --> src/main.rs:2:9 + | +2 | let (x, y) = (1, 2, 3); + | ^^^^^^ --------- this expression has type `({integer}, {integer}, {integer})` + | | + | expected a tuple with 3 elements, found one with 2 elements + | + = note: expected tuple `({integer}, {integer}, {integer})` + found tuple `(_, _)` +``` + +To fix the error, we could ignore one or more of the values in the tuple using +`_` or `..`, as you’ll see in the “Ignoring Values in a Pattern” 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 of elements in the tuple. + +### Function Parameters + +Function parameters can also be patterns. The code in Listing 18-6, which +declares a function named `foo` that takes one parameter named `x` of type +`i32`, should by now look familiar. + +``` +fn foo(x: i32) { + // code goes here +} +``` + +Listing 18-6: A function signature uses patterns in the parameters + +The `x` part is a pattern! As we did with `let`, we could match a tuple in a +function’s arguments to the pattern. Listing 18-7 splits the values in a tuple +as we pass it to a function. + +Filename: src/main.rs + +``` +fn print_coordinates(&(x, y): &(i32, i32)) { + println!("Current location: ({}, {})", x, y); +} + +fn main() { + let point = (3, 5); + print_coordinates(&point); +} +``` + +Listing 18-7: A function with parameters that destructure a tuple + +This code prints `Current location: (3, 5)`. The values `&(3, 5)` match the +pattern `&(x, y)`, so `x` is the value `3` and `y` is the value `5`. + +We can also use patterns in closure parameter lists in the same way as in +function parameter lists, because closures are similar to functions, as +discussed in Chapter 13. + +At this point, you’ve seen several ways of using patterns, but patterns don’t +work the same in every place we can use them. In some places, the patterns must +be irrefutable; in other circumstances, they can be refutable. We’ll discuss +these two concepts next. + +## Refutability: Whether a Pattern Might Fail to Match + +Patterns come in two forms: refutable and irrefutable. Patterns that will match +for any possible value passed are *irrefutable*. An example would be `x` in the +statement `let x = 5;` because `x` matches anything and therefore cannot fail +to match. Patterns that can fail to match for some possible value are +*refutable*. An example would be `Some(x)` in the expression `if let Some(x) = +a_value` because if the value in the `a_value` variable is `None` rather than +`Some`, the `Some(x)` pattern will not match. + +Function parameters, `let` statements, and `for` loops can only accept +irrefutable patterns, because the program cannot do anything meaningful when +values don’t match. The `if let` and `while let` expressions accept +refutable and irrefutable patterns, but the compiler warns against +irrefutable patterns because by definition they’re intended to handle possible +failure: the functionality of a conditional is in its ability to perform +differently depending on success or failure. + +In general, you shouldn’t have to worry about the distinction between refutable +and irrefutable patterns; however, you do need to be familiar with the concept +of refutability so you can respond when you see it in an error message. In +those cases, you’ll need to change either the pattern or the construct you’re +using the pattern with, depending on the intended behavior of the code. + +Let’s look at an example of what happens when we try to use a refutable pattern +where Rust requires an irrefutable pattern and vice versa. Listing 18-8 shows a +`let` statement, but for the pattern we’ve specified `Some(x)`, a refutable +pattern. As you might expect, this code will not compile. + +``` +let Some(x) = some_option_value; +``` + +Listing 18-8: Attempting to use a refutable pattern with `let` + +If `some_option_value` was a `None` value, it would fail to match the pattern +`Some(x)`, meaning the pattern is refutable. However, the `let` statement can +only accept an irrefutable pattern because there is nothing valid the code can +do with a `None` value. At compile time, Rust will complain that we’ve tried to +use a refutable pattern where an irrefutable pattern is required: + +``` +error[E0005]: refutable pattern in local binding: `None` not covered + --> src/main.rs:3:9 + | +3 | let Some(x) = some_option_value; + | ^^^^^^^ pattern `None` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `Option<i32>` +help: you might want to use `if let` to ignore the variant that isn't matched + | +3 | if let Some(x) = some_option_value { /* */ } + | +``` + +Because we didn’t cover (and couldn’t cover!) every valid value with the +pattern `Some(x)`, Rust rightfully produces a compiler error. + +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 doesn’t 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 let Some(x) = some_option_value { + println!("{}", x); +} +``` + +Listing 18-9: Using `if let` and a block with refutable patterns instead of +`let` + +We’ve given the code an out! This code is perfectly valid, although it means we +cannot use an irrefutable pattern without receiving an error. If we give `if +let` a pattern that will always match, such as `x`, as shown in Listing 18-10, +the compiler will give a warning. + +``` +if let x = 5 { + println!("{}", x); +}; +``` + +Listing 18-10: Attempting to use an irrefutable pattern with `if let` + +Rust complains that it doesn’t make sense to use `if let` with an irrefutable +pattern: + +``` +warning: irrefutable `if let` pattern + --> src/main.rs:2:8 + | +2 | if let x = 5 { + | ^^^^^^^^^ + | + = note: `#[warn(irrefutable_let_patterns)]` on by default + = note: this pattern will always match, so the `if let` is useless + = help: consider replacing the `if let` with a `let` +``` + +For this reason, match arms must use refutable patterns, except for the last +arm, which should match any remaining values with an irrefutable pattern. Rust +allows us to use an irrefutable pattern in a `match` with only one arm, but +this syntax isn’t particularly useful and could be replaced with a simpler +`let` statement. + +Now that you know where to use patterns and the difference between refutable +and irrefutable patterns, let’s cover all the syntax we can use to create +patterns. + +## Pattern Syntax + +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 + +As you saw in Chapter 6, you can match patterns against literals directly. The +following code gives some examples: + +``` +let x = 1; + +match x { + 1 => println!("one"), + 2 => println!("two"), + 3 => println!("three"), + _ => println!("anything"), +} +``` + +This code prints `one` because the value in `x` is 1. This syntax is useful +when you want your code to take an action if it gets a particular concrete +value. + +### Matching Named Variables + +Named variables are irrefutable patterns that match any value, and we’ve used +them many times in the book. However, there is a complication when you use +named variables in `match` expressions. Because `match` starts a new scope, +variables declared as part of a pattern inside the `match` expression will +shadow those with the same name outside the `match` construct, as is the case +with all variables. In Listing 18-11, we declare a variable named `x` with the +value `Some(5)` and a variable `y` with the value `10`. We then create a +`match` expression on the value `x`. Look at the patterns in the match arms and +`println!` at the end, and try to figure out what the code will print before +running this code or reading further. + +Filename: src/main.rs + +``` +fn main() { + [1] let x = Some(5); + [2] let y = 10; + + match x { + [3] Some(50) => println!("Got 50"), + [4] Some(y) => println!("Matched, y = {y}"), + [5] _ => println!("Default case, x = {:?}", x), + } + + [6] println!("at the end: x = {:?}, y = {y}", x); +} +``` + +Listing 18-11: A `match` expression with an arm that introduces a shadowed +variable `y` + +Let’s walk through what happens when the `match` expression runs. The pattern +in the first match arm [3] doesn’t match the defined value of `x` [1], so the +code continues. + +The pattern in the second match arm [4] introduces a new variable named `y` +that will match any value inside a `Some` value. Because we’re in a new scope +inside the `match` expression, this is a new `y` variable, not the `y` we +declared at the beginning with the value 10 [2]. This new `y` binding will +match any value inside a `Some`, which is what we have in `x`. Therefore, this +new `y` binds to the inner value of the `Some` in `x`. That value is `5`, so +the expression for that arm executes and prints `Matched, y = 5`. + +If `x` had been a `None` value instead of `Some(5)`, the patterns in the first +two arms wouldn’t have matched, so the value would have matched to the +underscore [5]. We didn’t introduce the `x` variable in the pattern of the +underscore arm, so the `x` in the expression is still the outer `x` that hasn’t +been shadowed. In this hypothetical case, the `match` would print `Default +case, x = None`. + +When the `match` expression is done, its scope ends, and so does the scope of +the inner `y`. The last `println!` [6] produces `at the end: x = Some(5), y = +10`. + +To create a `match` expression that compares the values of the outer `x` and +`y`, rather than introducing a shadowed variable, we would need to use a match +guard conditional instead. We’ll talk about match guards later in the “Extra +Conditionals with Match Guards” section. + +### Multiple Patterns + +In `match` expressions, you can match multiple patterns using the `|` syntax, +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 +arm’s code will run: + +``` +let x = 1; + +match x { + 1 | 2 => println!("one or two"), + 3 => println!("three"), + _ => println!("anything"), +} +``` + +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 given +range, that arm will execute: + +``` +let x = 5; + +match x { + 1..=5 => println!("one through five"), + _ => println!("something else"), +} +``` + +If `x` is 1, 2, 3, 4, or 5, the first arm will match. This syntax is more +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! + +The compiler checks that the range isn’t 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: + +``` +let x = 'c'; + +match x { + 'a'..='j' => println!("early ASCII letter"), + 'k'..='z' => println!("late ASCII letter"), + _ => println!("something else"), +} +``` + +Rust can tell that `'c'` is within the first pattern’s range and prints `early +ASCII letter`. + +### Destructuring to Break Apart Values + +We can also use patterns to destructure structs, enums, and tuples to use +different parts of these values. Let’s walk through each value. + +#### Destructuring Structs + +Listing 18-12 shows a `Point` struct with two fields, `x` and `y`, that we can +break apart using a pattern with a `let` statement. + +Filename: src/main.rs + +``` +struct Point { + x: i32, + y: i32, +} + +fn main() { + let p = Point { x: 0, y: 7 }; + + let Point { x: a, y: b } = p; + assert_eq!(0, a); + assert_eq!(7, b); +} +``` + +Listing 18-12: Destructuring a struct’s fields into separate variables + +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 don’t have to match the field names of the struct. +However, it’s 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`. + +Filename: src/main.rs + +``` +struct Point { + x: i32, + y: i32, +} + +fn main() { + let p = Point { x: 0, y: 7 }; + + let Point { x, y } = p; + assert_eq!(0, x); + assert_eq!(7, y); +} +``` + +Listing 18-13: Destructuring struct fields using struct field shorthand + +This code creates the variables `x` and `y` that match the `x` and `y` fields +of the `p` variable. The outcome is that the variables `x` and `y` contain the +values from the `p` struct. + +We can also destructure with literal values as part of the struct pattern +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. + +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. + +Filename: src/main.rs + +``` +fn main() { + let p = Point { x: 0, y: 7 }; + + match p { + Point { x, y: 0 } => println!("On the x axis at {}", x), + Point { x: 0, y } => println!("On the y axis at {}", y), + Point { x, y } => println!("On neither axis: ({}, {})", x, y), + } +} +``` + +Listing 18-14: Destructuring and matching literal values in one pattern + +The first arm will match any point that lies on the `x` axis by specifying that +the `y` field matches if its value matches the literal `0`. The pattern still +creates an `x` variable that we can use in the code for this arm. + +Similarly, the second arm matches any point on the `y` axis by specifying that +the `x` field matches if its value is `0` and creates a variable `y` for the +value of the `y` field. The third arm doesn’t specify any literals, so it +matches any other `Point` and creates variables for both the `x` and `y` fields. + +In this example, the value `p` matches the second arm by virtue of `x` +containing a 0, so this code will print `On the y axis at 7`. + +Remember that a `match` expression stops checking arms once it has found the +first matching pattern, so even though `Point { x: 0, y: 0}` is on the `x` axis +and the `y` axis, this code would only print `On the x axis at 0`. + +<!-- We should remind them that we stop at the first pattern, so even though +0,0 is on both x- and y-axis in a sense, you'll only ever see the "on x-axis +message" /JT --> +<!-- Done! /Carol --> + +#### Destructuring Enums + +We've destructured enums in this book (for example, Listing 6-5 in Chapter 6), +but haven’t 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. + +Filename: src/main.rs + +``` +enum Message { + Quit, + Move { x: i32, y: i32 }, + Write(String), + ChangeColor(i32, i32, i32), +} + +fn main() { + [1] let msg = Message::ChangeColor(0, 160, 255); + + match msg { + [2] Message::Quit => { + println!("The Quit variant has no data to destructure.") + } + [3] Message::Move { x, y } => { + println!( + "Move in the x direction {} and in the y direction {}", + x, y + ); + } + [4] Message::Write(text) => println!("Text message: {}", text), + [5] Message::ChangeColor(r, g, b) => println!( + "Change the color to red {}, green {}, and blue {}", + r, g, b + ), + } +} +``` + +Listing 18-15: Destructuring enum variants that hold different kinds of values + +This code will print `Change the color to red 0, green 160, and blue 255`. Try +changing the value of `msg` [1] to see the code from the other arms run. + +For enum variants without any data, like `Message::Quit` [2], we can’t +destructure the value any further. We can only match on the literal +`Message::Quit` value, and no variables are in that pattern. + +For struct-like enum variants, such as `Message::Move` [3], we can use a +pattern similar to the pattern we specify to match structs. After the variant +name, we place curly brackets and then list the fields with variables so we +break apart the pieces to use in the code for this arm. Here we use the +shorthand form as we did in Listing 18-13. + +For tuple-like enum variants, like `Message::Write` that holds a tuple with one +element [4] and `Message::ChangeColor` that holds a tuple with three elements +[5], the pattern is similar to the pattern we specify to match tuples. The +number of variables in the pattern must match the number of elements in the +variant we’re matching. + +#### Destructuring Nested Structs and Enums + +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. + +``` +enum Color { + Rgb(i32, i32, i32), + Hsv(i32, i32, i32), +} + +enum Message { + Quit, + Move { x: i32, y: i32 }, + Write(String), + ChangeColor(Color), +} + +fn main() { + let msg = Message::ChangeColor(Color::Hsv(0, 160, 255)); + + match msg { + Message::ChangeColor(Color::Rgb(r, g, b)) => println!( + "Change the color to red {}, green {}, and blue {}", + r, g, b + ), + Message::ChangeColor(Color::Hsv(h, s, v)) => println!( + "Change the color to hue {}, saturation {}, and value {}", + h, s, v + ), + _ => (), + } +} +``` + +Listing 18-16: Matching on nested enums + +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 `Color::Hsv` instead. We can specify these complex conditions in one +`match` expression, even though two enums are involved. + +#### Destructuring Structs and Tuples + +We can mix, match, and nest destructuring patterns in even more complex ways. +The following example shows a complicated destructure where we nest structs and +tuples inside a tuple and destructure all the primitive values out: + +``` +let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 }); +``` + +This code lets us break complex types into their component parts so we can use +the values we’re interested in separately. + +Destructuring with patterns is a convenient way to use pieces of values, such +as the value from each field in a struct, separately from each other. + +### Ignoring Values in a Pattern + +You’ve seen that it’s sometimes useful to ignore values in a pattern, such as +in the last arm of a `match`, to get a catchall that doesn’t actually do +anything but does account for all remaining possible values. There are a few +ways to ignore entire values or parts of values in a pattern: using the `_` +pattern (which you’ve seen), using the `_` pattern within another pattern, +using a name that starts with an underscore, or using `..` to ignore remaining +parts of a value. Let’s explore how and why to use each of these patterns. + +#### Ignoring an Entire Value with `_` + +We’ve 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. + +Filename: src/main.rs + +``` +fn foo(_: i32, y: i32) { + println!("This code only uses the y parameter: {}", y); +} + +fn main() { + foo(3, 4); +} +``` + +Listing 18-17: Using `_` in a function signature + +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 doesn’t include the unused parameter. Ignoring +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 doesn’t 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 `_` + +We can also use `_` inside another pattern to ignore just part of a value, for +example, when we want to test for only part of a value but have no use for the +other parts in the corresponding code we want to run. Listing 18-18 shows code +responsible for managing a setting’s value. The business requirements are that +the user should not be allowed to overwrite an existing customization of a +setting but can unset the setting and give it a value if it is currently unset. + +``` +let mut setting_value = Some(5); +let new_setting_value = Some(10); + +match (setting_value, new_setting_value) { + (Some(_), Some(_)) => { + println!("Can't overwrite an existing customized value"); + } + _ => { + setting_value = new_setting_value; + } +} + +println!("setting is {:?}", setting_value); +``` + +Listing 18-18: Using an underscore within patterns that match `Some` variants +when we don’t need to use the value inside the `Some` + +This code will print `Can't overwrite an existing customized value` and then +`setting is Some(5)`. In the first match arm, we don’t 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 the reason for not changing `setting_value`, and it doesn’t get +changed. + +In all other cases (if either `setting_value` or `new_setting_value` are +`None`) expressed by the `_` pattern in the second arm, we want to allow +`new_setting_value` to become `setting_value`. + +We can also use underscores in multiple places within one pattern to ignore +particular values. Listing 18-19 shows an example of ignoring the second and +fourth values in a tuple of five items. + +``` +let numbers = (2, 4, 8, 16, 32); + +match numbers { + (first, _, third, _, fifth) => { + println!("Some numbers: {first}, {third}, {fifth}") + } +} +``` + +Listing 18-19: Ignoring multiple parts of a tuple + +This code will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be +ignored. + +#### Ignoring an Unused Variable by Starting Its Name with `_` + +If you create a variable but don’t use it anywhere, Rust will usually issue a +warning because an unused variable could be a bug. However, sometimes it’s +useful to be able to create a variable you won’t use yet, such as when you’re +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. + +Filename: src/main.rs + +``` +fn main() { + let _x = 5; + let y = 10; +} +``` + +Listing 18-20: Starting a variable name with an +underscore to avoid getting unused variable warnings + +Here we get a warning about not using the variable `y`, but we don’t get a +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 +variable, whereas `_` doesn’t bind at all. To show a case where this +distinction matters, Listing 18-21 will provide us with an error. + +``` +let s = Some(String::from("Hello!")); + +if let Some(_s) = s { + println!("found a string"); +} + +println!("{:?}", s); +``` + +Listing 18-21: An unused variable starting with an underscore still binds the +value, which might take ownership of the value + +We’ll receive an error because the `s` value will still be moved into `_s`, +which prevents us from using `s` again. However, using the underscore by itself +doesn’t ever bind to the value. Listing 18-22 will compile without any errors +because `s` doesn’t get moved into `_`. + +``` +let s = Some(String::from("Hello!")); + +if let Some(_) = s { + println!("found a string"); +} + +println!("{:?}", s); +``` + +Listing 18-22: Using an underscore does not bind the value + +This code works just fine because we never bind `s` to anything; it isn’t moved. + +#### Ignoring Remaining Parts of a Value with `..` + +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 haven’t +explicitly matched in the rest of the pattern. In Listing 18-23, we have a +`Point` struct that holds a coordinate in three-dimensional space. In the +`match` expression, we want to operate only on the `x` coordinate and ignore +the values in the `y` and `z` fields. + +``` +struct Point { + x: i32, + y: i32, + z: i32, +} + +let origin = Point { x: 0, y: 0, z: 0 }; + +match origin { + Point { x, .. } => println!("x is {}", x), +} +``` + +Listing 18-23: Ignoring all fields of a `Point` except for `x` by using `..` + +We list the `x` value and then just include the `..` pattern. This is quicker +than having to list `y: _` and `z: _`, particularly when we’re working with +structs that have lots of fields in situations where only one or two fields are +relevant. + +The syntax `..` will expand to as many values as it needs to be. Listing 18-24 +shows how to use `..` with a tuple. + +Filename: src/main.rs + +``` +fn main() { + let numbers = (2, 4, 8, 16, 32); + + match numbers { + (first, .., last) => { + println!("Some numbers: {first}, {last}"); + } + } +} +``` + +Listing 18-24: Matching only the first and last values in a tuple and ignoring +all other values + +In this code, the first and last value are matched with `first` and `last`. The +`..` will match and ignore everything in the middle. + +However, using `..` must be unambiguous. If it is unclear which values are +intended for matching and which should be ignored, Rust will give us an error. +Listing 18-25 shows an example of using `..` ambiguously, so it will not +compile. + +Filename: src/main.rs + +``` +fn main() { + let numbers = (2, 4, 8, 16, 32); + + match numbers { + (.., second, ..) => { + println!("Some numbers: {}", second) + }, + } +} +``` + +Listing 18-25: An attempt to use `..` in an ambiguous way + +When we compile this example, we get this error: + +``` +error: `..` can only be used once per tuple pattern + --> src/main.rs:5:22 + | +5 | (.., second, ..) => { + | -- ^^ can only be used once per tuple pattern + | | + | previously used here +``` + +It’s impossible for Rust to determine how many values in the tuple to ignore +before matching a value with `second` and then how many further values to +ignore thereafter. This code could mean that we want to ignore `2`, bind +`second` to `4`, and then ignore `8`, `16`, and `32`; or that we want to ignore +`2` and `4`, bind `second` to `8`, and then ignore `16` and `32`; and so forth. +The variable name `second` doesn’t mean anything special to Rust, so we get a +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 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 +guard of `if x % 2 == 0` (which will be true if the number is even). + +``` +let num = Some(4); + +match num { + Some(x) if x % 2 == 0 => println!("The number {} is even", x), + Some(x) => println!("The number {} is odd", x), + None => (), +} +``` + +Listing 18-26: Adding a match guard to a pattern + +This example will print `The number 4 is even`. When `num` is compared to the +pattern in the first arm, it matches, because `Some(4)` matches `Some(x)`. Then +the match guard checks whether the remainder of dividing `x` by 2 is equal to +0, and because it is, the first arm is selected. + +If `num` had been `Some(5)` instead, the match guard in the first arm would +have been false because the remainder of 5 divided by 2 is 1, which is not +equal to 0. Rust would then go to the second arm, which would match because the +second arm doesn’t have a match guard and therefore matches any `Some` variant. + +There is no way to express the `if x % 2 == 0` condition within a pattern, so +the match guard gives us the ability to express this logic. The downside of +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 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 couldn’t test against the value of the +outer variable. Listing 18-27 shows how we can use a match guard to fix this +problem. + +Filename: src/main.rs + +``` +fn main() { + let x = Some(5); + let y = 10; + + match x { + Some(50) => println!("Got 50"), + Some(n) if n == y => println!("Matched, n = {n}"), + _ => println!("Default case, x = {:?}", x), + } + + println!("at the end: x = {:?}, y = {y}", x); +} +``` + +Listing 18-27: Using a match guard to test for equality with an outer variable + +This code will now print `Default case, x = Some(5)`. The pattern in the second +match arm doesn’t introduce a new variable `y` that would shadow the outer `y`, +meaning we can use the outer `y` in the match guard. Instead of specifying the +pattern as `Some(y)`, which would have shadowed the outer `y`, we specify +`Some(n)`. This creates a new variable `n` that doesn’t shadow anything because +there is no `n` variable outside the `match`. + +The match guard `if n == y` is not a pattern and therefore doesn’t introduce +new variables. This `y` *is* the outer `y` rather than a new shadowed `y`, and +we can look for a value that has the same value as the outer `y` by comparing +`n` to `y`. + +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 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`. + +``` +let x = 4; +let y = false; + +match x { + 4 | 5 | 6 if y => println!("yes"), + _ => println!("no"), +} +``` + +Listing 18-28: Combining multiple patterns with a match guard + +The match condition states that the arm only matches if the value of `x` is +equal to `4`, `5`, or `6` *and* if `y` is `true`. When this code runs, the +pattern of the first arm matches because `x` is `4`, but the match guard `if y` +is false, so the first arm is not chosen. The code moves on to the second arm, +which does match, and this program prints `no`. The reason is that the `if` +condition applies to the whole pattern `4 | 5 | 6`, not only to the last value +`6`. In other words, the precedence of a match guard in relation to a pattern +behaves like this: + +``` +(4 | 5 | 6) if y => ... +``` + +rather than this: + +``` +4 | 5 | (6 if y) => ... +``` + +After running the code, the precedence behavior is evident: if the match guard +were applied only to the final value in the list of values specified using the +`|` operator, the arm would have matched and the program would have printed +`yes`. + +### `@` Bindings + +The *at* operator `@` lets us create a variable that holds a value at the same +time as we’re 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 we’ll use a different name. + +``` +enum Message { + Hello { id: i32 }, +} + +let msg = Message::Hello { id: 5 }; + +match msg { + Message::Hello { + id: id_variable @ 3..=7, + } => println!("Found an id in range: {}", id_variable), + Message::Hello { id: 10..=12 } => { + println!("Found an id in another range") + } + Message::Hello { id } => println!("Found some other id: {}", id), +} +``` + +Listing 18-29: Using `@` to bind to a value in a pattern while also testing it + +This example will print `Found an id in range: 5`. By specifying `id_variable +@` before the range `3..=7`, we’re capturing whatever value matched the range +while also testing that the value matched the range pattern. + +In the second arm, where we only have a range specified in the pattern, the code +associated with the arm doesn’t have a variable that contains the actual value +of the `id` field. The `id` field’s value could have been 10, 11, or 12, but +the code that goes with that pattern doesn’t know which it is. The pattern code +isn’t able to use the value from the `id` field, because we haven’t saved the +`id` value in a variable. + +In the last arm, where we’ve specified a variable without a range, we do have +the value available to use in the arm’s code in a variable named `id`. The +reason is that we’ve used the struct field shorthand syntax. But we haven’t +applied any test to the value in the `id` field in this arm, as we did with the +first two arms: any value would match this pattern. + +Using `@` lets us test a value and save it in a variable within one pattern. + +## Summary + +Rust’s 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 won’t 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, we’ll look at some advanced +aspects of a variety of Rust’s features. |