]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## The `match` Control Flow Operator |
2 | ||
3 | Rust has an extremely powerful control flow operator called `match` that allows | |
4 | you to compare a value against a series of patterns and then execute code based | |
5 | on which pattern matches. Patterns can be made up of literal values, variable | |
6 | names, wildcards, and many other things; Chapter 18 covers all the different | |
7 | kinds of patterns and what they do. The power of `match` comes from the | |
8 | expressiveness of the patterns and the fact that the compiler confirms that all | |
9 | possible cases are handled. | |
10 | ||
11 | Think of a `match` expression as being like a coin-sorting machine: coins slide | |
12 | down a track with variously sized holes along it, and each coin falls through | |
13 | the first hole it encounters that it fits into. In the same way, values go | |
14 | through each pattern in a `match`, and at the first pattern the value “fits,” | |
15 | the value falls into the associated code block to be used during execution. | |
16 | ||
17 | Because we just mentioned coins, let’s use them as an example using `match`! We | |
18 | can write a function that can take an unknown United States coin and, in a | |
19 | similar way as the counting machine, determine which coin it is and return its | |
69743fb6 | 20 | value in cents, as shown here in Listing 6-3. |
13cf67c4 XL |
21 | |
22 | ```rust | |
74b04a01 | 23 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-03/src/main.rs:here}} |
13cf67c4 XL |
24 | ``` |
25 | ||
26 | <span class="caption">Listing 6-3: An enum and a `match` expression that has | |
27 | the variants of the enum as its patterns</span> | |
28 | ||
29 | Let’s break down the `match` in the `value_in_cents` function. First, we list | |
30 | the `match` keyword followed by an expression, which in this case is the value | |
31 | `coin`. This seems very similar to an expression used with `if`, but there’s a | |
32 | big difference: with `if`, the expression needs to return a Boolean value, but | |
33 | here, it can be any type. The type of `coin` in this example is the `Coin` enum | |
34 | that we defined on line 1. | |
35 | ||
36 | Next are the `match` arms. An arm has two parts: a pattern and some code. The | |
37 | first arm here has a pattern that is the value `Coin::Penny` and then the `=>` | |
38 | operator that separates the pattern and the code to run. The code in this case | |
39 | is just the value `1`. Each arm is separated from the next with a comma. | |
40 | ||
41 | When the `match` expression executes, it compares the resulting value against | |
42 | the pattern of each arm, in order. If a pattern matches the value, the code | |
43 | associated with that pattern is executed. If that pattern doesn’t match the | |
44 | value, execution continues to the next arm, much as in a coin-sorting machine. | |
45 | We can have as many arms as we need: in Listing 6-3, our `match` has four arms. | |
46 | ||
47 | The code associated with each arm is an expression, and the resulting value of | |
48 | the expression in the matching arm is the value that gets returned for the | |
49 | entire `match` expression. | |
50 | ||
51 | Curly brackets typically aren’t used if the match arm code is short, as it is | |
52 | in Listing 6-3 where each arm just returns a value. If you want to run multiple | |
53 | lines of code in a match arm, you can use curly brackets. For example, the | |
54 | following code would print “Lucky penny!” every time the method was called with | |
55 | a `Coin::Penny` but would still return the last value of the block, `1`: | |
56 | ||
57 | ```rust | |
74b04a01 | 58 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-08-match-arm-multiple-lines/src/main.rs:here}} |
13cf67c4 XL |
59 | ``` |
60 | ||
61 | ### Patterns that Bind to Values | |
62 | ||
63 | Another useful feature of match arms is that they can bind to the parts of the | |
64 | values that match the pattern. This is how we can extract values out of enum | |
65 | variants. | |
66 | ||
67 | As an example, let’s change one of our enum variants to hold data inside it. | |
68 | From 1999 through 2008, the United States minted quarters with different | |
69 | designs for each of the 50 states on one side. No other coins got state | |
70 | designs, so only quarters have this extra value. We can add this information to | |
71 | our `enum` by changing the `Quarter` variant to include a `UsState` value stored | |
69743fb6 | 72 | inside it, which we’ve done here in Listing 6-4. |
13cf67c4 XL |
73 | |
74 | ```rust | |
74b04a01 | 75 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-04/src/main.rs:here}} |
13cf67c4 XL |
76 | ``` |
77 | ||
78 | <span class="caption">Listing 6-4: A `Coin` enum in which the `Quarter` variant | |
79 | also holds a `UsState` value</span> | |
80 | ||
81 | Let’s imagine that a friend of ours is trying to collect all 50 state quarters. | |
82 | While we sort our loose change by coin type, we’ll also call out the name of | |
83 | the state associated with each quarter so if it’s one our friend doesn’t have, | |
84 | they can add it to their collection. | |
85 | ||
86 | In the match expression for this code, we add a variable called `state` to the | |
87 | pattern that matches values of the variant `Coin::Quarter`. When a | |
88 | `Coin::Quarter` matches, the `state` variable will bind to the value of that | |
89 | quarter’s state. Then we can use `state` in the code for that arm, like so: | |
90 | ||
91 | ```rust | |
74b04a01 | 92 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-09-variable-in-pattern/src/main.rs:here}} |
13cf67c4 XL |
93 | ``` |
94 | ||
95 | If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` | |
96 | would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each | |
97 | of the match arms, none of them match until we reach `Coin::Quarter(state)`. At | |
98 | that point, the binding for `state` will be the value `UsState::Alaska`. We can | |
99 | then use that binding in the `println!` expression, thus getting the inner | |
100 | state value out of the `Coin` enum variant for `Quarter`. | |
101 | ||
102 | ### Matching with `Option<T>` | |
103 | ||
104 | In the previous section, we wanted to get the inner `T` value out of the `Some` | |
105 | case when using `Option<T>`; we can also handle `Option<T>` using `match` as we | |
106 | did with the `Coin` enum! Instead of comparing coins, we’ll compare the | |
107 | variants of `Option<T>`, but the way that the `match` expression works remains | |
108 | the same. | |
109 | ||
110 | Let’s say we want to write a function that takes an `Option<i32>` and, if | |
111 | there’s a value inside, adds 1 to that value. If there isn’t a value inside, | |
112 | the function should return the `None` value and not attempt to perform any | |
113 | operations. | |
114 | ||
115 | This function is very easy to write, thanks to `match`, and will look like | |
69743fb6 | 116 | Listing 6-5. |
13cf67c4 XL |
117 | |
118 | ```rust | |
74b04a01 | 119 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:here}} |
13cf67c4 XL |
120 | ``` |
121 | ||
122 | <span class="caption">Listing 6-5: A function that uses a `match` expression on | |
123 | an `Option<i32>`</span> | |
124 | ||
125 | Let’s examine the first execution of `plus_one` in more detail. When we call | |
126 | `plus_one(five)`, the variable `x` in the body of `plus_one` will have the | |
127 | value `Some(5)`. We then compare that against each match arm. | |
128 | ||
129 | ```rust,ignore | |
74b04a01 | 130 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:first_arm}} |
13cf67c4 XL |
131 | ``` |
132 | ||
133 | The `Some(5)` value doesn’t match the pattern `None`, so we continue to the | |
134 | next arm. | |
135 | ||
136 | ```rust,ignore | |
74b04a01 | 137 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:second_arm}} |
13cf67c4 XL |
138 | ``` |
139 | ||
140 | Does `Some(5)` match `Some(i)`? Why yes it does! We have the same variant. The | |
141 | `i` binds to the value contained in `Some`, so `i` takes the value `5`. The | |
142 | code in the match arm is then executed, so we add 1 to the value of `i` and | |
143 | create a new `Some` value with our total `6` inside. | |
144 | ||
145 | Now let’s consider the second call of `plus_one` in Listing 6-5, where `x` is | |
146 | `None`. We enter the `match` and compare to the first arm. | |
147 | ||
148 | ```rust,ignore | |
74b04a01 | 149 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:first_arm}} |
13cf67c4 XL |
150 | ``` |
151 | ||
152 | It matches! There’s no value to add to, so the program stops and returns the | |
153 | `None` value on the right side of `=>`. Because the first arm matched, no other | |
154 | arms are compared. | |
155 | ||
156 | Combining `match` and enums is useful in many situations. You’ll see this | |
157 | pattern a lot in Rust code: `match` against an enum, bind a variable to the | |
158 | data inside, and then execute code based on it. It’s a bit tricky at first, but | |
159 | once you get used to it, you’ll wish you had it in all languages. It’s | |
160 | consistently a user favorite. | |
161 | ||
162 | ### Matches Are Exhaustive | |
163 | ||
164 | There’s one other aspect of `match` we need to discuss. Consider this version | |
165 | of our `plus_one` function that has a bug and won’t compile: | |
166 | ||
167 | ```rust,ignore,does_not_compile | |
74b04a01 | 168 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/src/main.rs:here}} |
13cf67c4 XL |
169 | ``` |
170 | ||
171 | We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s | |
172 | a bug Rust knows how to catch. If we try to compile this code, we’ll get this | |
173 | error: | |
174 | ||
175 | ```text | |
74b04a01 | 176 | {{#include ../listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt}} |
13cf67c4 XL |
177 | ``` |
178 | ||
179 | Rust knows that we didn’t cover every possible case and even knows which | |
180 | pattern we forgot! Matches in Rust are *exhaustive*: we must exhaust every last | |
181 | possibility in order for the code to be valid. Especially in the case of | |
182 | `Option<T>`, when Rust prevents us from forgetting to explicitly handle the | |
183 | `None` case, it protects us from assuming that we have a value when we might | |
ba9703b0 | 184 | have null, thus making the billion-dollar mistake discussed earlier impossible. |
13cf67c4 XL |
185 | |
186 | ### The `_` Placeholder | |
187 | ||
188 | Rust also has a pattern we can use when we don’t want to list all possible | |
189 | values. For example, a `u8` can have valid values of 0 through 255. If we only | |
190 | care about the values 1, 3, 5, and 7, we don’t want to have to list out 0, 2, | |
191 | 4, 6, 8, 9 all the way up to 255. Fortunately, we don’t have to: we can use the | |
192 | special pattern `_` instead: | |
193 | ||
194 | ```rust | |
74b04a01 | 195 | {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-11-underscore-placeholder/src/main.rs:here}} |
13cf67c4 XL |
196 | ``` |
197 | ||
198 | The `_` pattern will match any value. By putting it after our other arms, the | |
199 | `_` will match all the possible cases that aren’t specified before it. The `()` | |
200 | is just the unit value, so nothing will happen in the `_` case. As a result, we | |
201 | can say that we want to do nothing for all the possible values that we don’t | |
202 | list before the `_` placeholder. | |
203 | ||
204 | However, the `match` expression can be a bit wordy in a situation in which we | |
69743fb6 | 205 | care about only *one* of the cases. For this situation, Rust provides `if let`. |
ba9703b0 XL |
206 | |
207 | More about patterns, and matching can be found in [chapter 18][ch18-00-patterns]. | |
208 | ||
209 | [ch18-00-patterns]: | |
210 | ch18-00-patterns.html |