diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/doc/book/src/ch06-02-match.md | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/doc/book/src/ch06-02-match.md')
-rw-r--r-- | src/doc/book/src/ch06-02-match.md | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/doc/book/src/ch06-02-match.md b/src/doc/book/src/ch06-02-match.md new file mode 100644 index 000000000..a24936829 --- /dev/null +++ b/src/doc/book/src/ch06-02-match.md @@ -0,0 +1,252 @@ +<a id="the-match-control-flow-operator"></a> +## The `match` Control Flow Construct + +Rust has an extremely powerful control flow construct called `match` that allows +you to compare a value against a series of patterns and then execute code based +on which pattern matches. Patterns can be made up of literal values, variable +names, wildcards, and many other things; Chapter 18 covers all the different +kinds of patterns and what they do. The power of `match` comes from the +expressiveness of the patterns and the fact that the compiler confirms that all +possible cases are handled. + +Think of a `match` expression as being like a coin-sorting machine: coins slide +down a track with variously sized holes along it, and each coin falls through +the first hole it encounters that it fits into. In the same way, values go +through each pattern in a `match`, and at the first pattern the value “fits,” +the value falls into the associated code block to be used during execution. + +Speaking of coins, let’s use them as an example using `match`! We can write a +function that takes an unknown United States coin and, in a similar way as the +counting machine, determines which coin it is and return its value in cents, as +shown here in Listing 6-3. + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-03/src/main.rs:here}} +``` + +<span class="caption">Listing 6-3: An enum and a `match` expression that has +the variants of the enum as its patterns</span> + +Let’s break down the `match` in the `value_in_cents` function. First, we list +the `match` keyword followed by an expression, which in this case is the value +`coin`. This seems very similar to an expression used with `if`, but there’s a +big difference: with `if`, the expression needs to return a Boolean value, but +here, it can return any type. The type of `coin` in this example is the `Coin` +enum that we defined on the first line. + +Next are the `match` arms. An arm has two parts: a pattern and some code. The +first arm here has a pattern that is the value `Coin::Penny` and then the `=>` +operator that separates the pattern and the code to run. The code in this case +is just the value `1`. Each arm is separated from the next with a comma. + +When the `match` expression executes, it compares the resulting value against +the pattern of each arm, in order. If a pattern matches the value, the code +associated with that pattern is executed. If that pattern doesn’t match the +value, execution continues to the next arm, much as in a coin-sorting machine. +We can have as many arms as we need: in Listing 6-3, our `match` has four arms. + +The code associated with each arm is an expression, and the resulting value of +the expression in the matching arm is the value that gets returned for the +entire `match` expression. + +We don’t typically use curly brackets if the match arm code is short, as it is +in Listing 6-3 where each arm just returns a value. If you want to run multiple +lines of code in a match arm, you must use curly brackets, and the comma +following the arm is then optional. For example, the following code prints +“Lucky penny!” every time the method is called with a `Coin::Penny`, but still +returns the last value of the block, `1`: + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-08-match-arm-multiple-lines/src/main.rs:here}} +``` + +### Patterns that Bind to Values + +Another useful feature of match arms is that they can bind to the parts of the +values that match the pattern. This is how we can extract values out of enum +variants. + +As an example, let’s change one of our enum variants to hold data inside it. +From 1999 through 2008, the United States minted quarters with different +designs for each of the 50 states on one side. No other coins got state +designs, so only quarters have this extra value. We can add this information to +our `enum` by changing the `Quarter` variant to include a `UsState` value stored +inside it, which we’ve done here in Listing 6-4. + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-04/src/main.rs:here}} +``` + +<span class="caption">Listing 6-4: A `Coin` enum in which the `Quarter` variant +also holds a `UsState` value</span> + +Let’s imagine that a friend is trying to collect all 50 state quarters. While +we sort our loose change by coin type, we’ll also call out the name of the +state associated with each quarter so if it’s one our friend doesn’t have, they +can add it to their collection. + +In the match expression for this code, we add a variable called `state` to the +pattern that matches values of the variant `Coin::Quarter`. When a +`Coin::Quarter` matches, the `state` variable will bind to the value of that +quarter’s state. Then we can use `state` in the code for that arm, like so: + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-09-variable-in-pattern/src/main.rs:here}} +``` + +If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` +would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each +of the match arms, none of them match until we reach `Coin::Quarter(state)`. At +that point, the binding for `state` will be the value `UsState::Alaska`. We can +then use that binding in the `println!` expression, thus getting the inner +state value out of the `Coin` enum variant for `Quarter`. + +### Matching with `Option<T>` + +In the previous section, we wanted to get the inner `T` value out of the `Some` +case when using `Option<T>`; we can also handle `Option<T>` using `match` as we +did with the `Coin` enum! Instead of comparing coins, we’ll compare the +variants of `Option<T>`, but the way that the `match` expression works remains +the same. + +Let’s say we want to write a function that takes an `Option<i32>` and, if +there’s a value inside, adds 1 to that value. If there isn’t a value inside, +the function should return the `None` value and not attempt to perform any +operations. + +This function is very easy to write, thanks to `match`, and will look like +Listing 6-5. + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:here}} +``` + +<span class="caption">Listing 6-5: A function that uses a `match` expression on +an `Option<i32>`</span> + +Let’s examine the first execution of `plus_one` in more detail. When we call +`plus_one(five)`, the variable `x` in the body of `plus_one` will have the +value `Some(5)`. We then compare that against each match arm. + +```rust,ignore +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:first_arm}} +``` + +The `Some(5)` value doesn’t match the pattern `None`, so we continue to the +next arm. + +```rust,ignore +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:second_arm}} +``` + +Does `Some(5)` match `Some(i)`? Why yes it does! We have the same variant. The +`i` binds to the value contained in `Some`, so `i` takes the value `5`. The +code in the match arm is then executed, so we add 1 to the value of `i` and +create a new `Some` value with our total `6` inside. + +Now let’s consider the second call of `plus_one` in Listing 6-5, where `x` is +`None`. We enter the `match` and compare to the first arm. + +```rust,ignore +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:first_arm}} +``` + +It matches! There’s no value to add to, so the program stops and returns the +`None` value on the right side of `=>`. Because the first arm matched, no other +arms are compared. + +Combining `match` and enums is useful in many situations. You’ll see this +pattern a lot in Rust code: `match` against an enum, bind a variable to the +data inside, and then execute code based on it. It’s a bit tricky at first, but +once you get used to it, you’ll wish you had it in all languages. It’s +consistently a user favorite. + +### Matches Are Exhaustive + +There’s one other aspect of `match` we need to discuss: the arms’ patterns must +cover all possibilities. Consider this version of our `plus_one` function, +which has a bug and won’t compile: + +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/src/main.rs:here}} +``` + +We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s +a bug Rust knows how to catch. If we try to compile this code, we’ll get this +error: + +```console +{{#include ../listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt}} +``` + +Rust knows that we didn’t cover every possible case and even knows which +pattern we forgot! Matches in Rust are *exhaustive*: we must exhaust every last +possibility in order for the code to be valid. Especially in the case of +`Option<T>`, when Rust prevents us from forgetting to explicitly handle the +`None` case, it protects us from assuming that we have a value when we might +have null, thus making the billion-dollar mistake discussed earlier impossible. + +### Catch-all Patterns and the `_` Placeholder + +Using enums, we can also take special actions for a few particular values, but +for all other values take one default action. Imagine we’re implementing a game +where, if you roll a 3 on a dice roll, your player doesn’t move, but instead +gets a new fancy hat. If you roll a 7, your player loses a fancy hat. For all +other values, your player moves that number of spaces on the game board. Here’s +a `match` that implements that logic, with the result of the dice roll +hardcoded rather than a random value, and all other logic represented by +functions without bodies because actually implementing them is out of scope for +this example: + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-15-binding-catchall/src/main.rs:here}} +``` + +For the first two arms, the patterns are the literal values 3 and 7. For the +last arm that covers every other possible value, the pattern is the variable +we’ve chosen to name `other`. The code that runs for the `other` arm uses the +variable by passing it to the `move_player` function. + +This code compiles, even though we haven’t listed all the possible values a +`u8` can have, because the last pattern will match all values not specifically +listed. This catch-all pattern meets the requirement that `match` must be +exhaustive. Note that we have to put the catch-all arm last because the +patterns are evaluated in order. If we put the catch-all arm earlier, the other +arms would never run, so Rust will warn us if we add arms after a catch-all! + +Rust also has a pattern we can use when we want a catch-all but don’t want to +*use* the value in the catch-all pattern: `_` is a special pattern that matches +any value and does not bind to that value. This tells Rust we aren’t going to +use the value, so Rust won’t warn us about an unused variable. + +Let’s change the rules of the game: now, if you roll anything other than a 3 or +a 7, you must roll again. We no longer need to use the catch-all value, so we +can change our code to use `_` instead of the variable named `other`: + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-16-underscore-catchall/src/main.rs:here}} +``` + +This example also meets the exhaustiveness requirement because we’re explicitly +ignoring all other values in the last arm; we haven’t forgotten anything. + +Finally, we’ll change the rules of the game one more time, so that nothing else +happens on your turn if you roll anything other than a 3 or a 7. We can express +that by using the unit value (the empty tuple type we mentioned in [“The Tuple +Type”][tuples]<!-- ignore --> section) as the code that goes with the `_` arm: + +```rust +{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-17-underscore-unit/src/main.rs:here}} +``` + +Here, we’re telling Rust explicitly that we aren’t going to use any other value +that doesn’t match a pattern in an earlier arm, and we don’t want to run any +code in this case. + +There’s more about patterns and matching that we’ll cover in [Chapter +18][ch18-00-patterns]<!-- ignore -->. For now, we’re going to move on to the +`if let` syntax, which can be useful in situations where the `match` expression +is a bit wordy. + +[tuples]: ch03-02-data-types.html#the-tuple-type +[ch18-00-patterns]: ch18-00-patterns.html |