]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | ## The `match` Control Flow Operator |
2 | ||
3 | Rust has an extremely powerful control-flow operator called `match` that allows | |
4 | us 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 | |
abe05a73 XL |
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 compiler checks that make sure all | |
cc61c64b XL |
9 | possible cases are handled. |
10 | ||
11 | Think of a `match` expression kind of 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 will fall 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 | |
20 | value in cents, as shown here in Listing 6-3: | |
21 | ||
22 | ```rust | |
23 | enum Coin { | |
24 | Penny, | |
25 | Nickel, | |
26 | Dime, | |
27 | Quarter, | |
28 | } | |
29 | ||
041b39d2 | 30 | fn value_in_cents(coin: Coin) -> u32 { |
cc61c64b XL |
31 | match coin { |
32 | Coin::Penny => 1, | |
33 | Coin::Nickel => 5, | |
34 | Coin::Dime => 10, | |
35 | Coin::Quarter => 25, | |
36 | } | |
37 | } | |
38 | ``` | |
39 | ||
40 | <span class="caption">Listing 6-3: An enum and a `match` expression that has | |
41 | the variants of the enum as its patterns.</span> | |
42 | ||
43 | Let’s break down the `match` in the `value_in_cents` function. First, we list | |
44 | the `match` keyword followed by an expression, which in this case is the value | |
45 | `coin`. This seems very similar to an expression used with `if`, but there’s a | |
46 | big difference: with `if`, the expression needs to return a boolean value. | |
47 | Here, it can be any type. The type of `coin` in this example is the `Coin` enum | |
48 | that we defined in Listing 6-3. | |
49 | ||
50 | Next are the `match` arms. An arm has two parts: a pattern and some code. The | |
51 | first arm here has a pattern that is the value `Coin::Penny` and then the `=>` | |
52 | operator that separates the pattern and the code to run. The code in this case | |
53 | is just the value `1`. Each arm is separated from the next with a comma. | |
54 | ||
55 | When the `match` expression executes, it compares the resulting value against | |
56 | the pattern of each arm, in order. If a pattern matches the value, the code | |
57 | associated with that pattern is executed. If that pattern doesn’t match the | |
58 | value, execution continues to the next arm, much like a coin sorting machine. | |
59 | We can have as many arms as we need: in Listing 6-3, our `match` has four arms. | |
60 | ||
61 | The code associated with each arm is an expression, and the resulting value of | |
62 | the expression in the matching arm is the value that gets returned for the | |
63 | entire `match` expression. | |
64 | ||
ea8adc8c XL |
65 | Curly brackets typically aren’t used if the match arm code is short, as it is |
66 | in Listing 6-3 where each arm just returns a value. If you want to run multiple | |
67 | lines of code in a match arm, you can use curly brackets. For example, the | |
cc61c64b XL |
68 | following code would print out “Lucky penny!” every time the method was called |
69 | with a `Coin::Penny` but would still return the last value of the block, `1`: | |
70 | ||
71 | ```rust | |
72 | # enum Coin { | |
73 | # Penny, | |
74 | # Nickel, | |
75 | # Dime, | |
76 | # Quarter, | |
77 | # } | |
78 | # | |
041b39d2 | 79 | fn value_in_cents(coin: Coin) -> u32 { |
cc61c64b XL |
80 | match coin { |
81 | Coin::Penny => { | |
82 | println!("Lucky penny!"); | |
83 | 1 | |
84 | }, | |
85 | Coin::Nickel => 5, | |
86 | Coin::Dime => 10, | |
87 | Coin::Quarter => 25, | |
88 | } | |
89 | } | |
90 | ``` | |
91 | ||
92 | ### Patterns that Bind to Values | |
93 | ||
94 | Another useful feature of match arms is that they can bind to parts of the | |
95 | values that match the pattern. This is how we can extract values out of enum | |
96 | variants. | |
97 | ||
98 | As an example, let’s change one of our enum variants to hold data inside it. | |
7cac9316 | 99 | From 1999 through 2008, the United States minted quarters with different |
cc61c64b XL |
100 | designs for each of the 50 states on one side. No other coins got state |
101 | designs, so only quarters have this extra value. We can add this information to | |
ea8adc8c | 102 | our `enum` by changing the `Quarter` variant to include a `UsState` value stored |
3b2f2976 | 103 | inside it, which we’ve done here in Listing 6-4: |
cc61c64b XL |
104 | |
105 | ```rust | |
106 | #[derive(Debug)] // So we can inspect the state in a minute | |
107 | enum UsState { | |
108 | Alabama, | |
109 | Alaska, | |
110 | // ... etc | |
111 | } | |
112 | ||
113 | enum Coin { | |
114 | Penny, | |
115 | Nickel, | |
116 | Dime, | |
117 | Quarter(UsState), | |
118 | } | |
119 | ``` | |
120 | ||
121 | <span class="caption">Listing 6-4: A `Coin` enum where the `Quarter` variant | |
122 | also holds a `UsState` value</span> | |
123 | ||
124 | Let’s imagine that a friend of ours is trying to collect all 50 state quarters. | |
125 | While we sort our loose change by coin type, we’ll also call out the name of | |
126 | the state associated with each quarter so if it’s one our friend doesn’t have, | |
127 | they can add it to their collection. | |
128 | ||
129 | In the match expression for this code, we add a variable called `state` to the | |
130 | pattern that matches values of the variant `Coin::Quarter`. When a | |
131 | `Coin::Quarter` matches, the `state` variable will bind to the value of that | |
132 | quarter’s state. Then we can use `state` in the code for that arm, like so: | |
133 | ||
134 | ```rust | |
135 | # #[derive(Debug)] | |
136 | # enum UsState { | |
137 | # Alabama, | |
138 | # Alaska, | |
139 | # } | |
140 | # | |
141 | # enum Coin { | |
142 | # Penny, | |
143 | # Nickel, | |
144 | # Dime, | |
145 | # Quarter(UsState), | |
146 | # } | |
147 | # | |
041b39d2 | 148 | fn value_in_cents(coin: Coin) -> u32 { |
cc61c64b XL |
149 | match coin { |
150 | Coin::Penny => 1, | |
151 | Coin::Nickel => 5, | |
152 | Coin::Dime => 10, | |
153 | Coin::Quarter(state) => { | |
154 | println!("State quarter from {:?}!", state); | |
155 | 25 | |
156 | }, | |
157 | } | |
158 | } | |
159 | ``` | |
160 | ||
161 | If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` | |
162 | would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each | |
163 | of the match arms, none of them match until we reach `Coin::Quarter(state)`. At | |
164 | that point, the binding for `state` will be the value `UsState::Alaska`. We can | |
165 | then use that binding in the `println!` expression, thus getting the inner | |
166 | state value out of the `Coin` enum variant for `Quarter`. | |
167 | ||
168 | ### Matching with `Option<T>` | |
169 | ||
170 | In the previous section we wanted to get the inner `T` value out of the `Some` | |
171 | case when using `Option<T>`; we can also handle `Option<T>` using `match` as we | |
172 | did with the `Coin` enum! Instead of comparing coins, we’ll compare the | |
173 | variants of `Option<T>`, but the way that the `match` expression works remains | |
174 | the same. | |
175 | ||
176 | Let’s say we want to write a function that takes an `Option<i32>`, and if | |
177 | there’s a value inside, adds one to that value. If there isn’t a value inside, | |
178 | the function should return the `None` value and not attempt to perform any | |
179 | operations. | |
180 | ||
181 | This function is very easy to write, thanks to `match`, and will look like | |
182 | Listing 6-5: | |
183 | ||
184 | ```rust | |
185 | fn plus_one(x: Option<i32>) -> Option<i32> { | |
186 | match x { | |
187 | None => None, | |
188 | Some(i) => Some(i + 1), | |
189 | } | |
190 | } | |
191 | ||
192 | let five = Some(5); | |
193 | let six = plus_one(five); | |
194 | let none = plus_one(None); | |
195 | ``` | |
196 | ||
197 | <span class="caption">Listing 6-5: A function that uses a `match` expression on | |
198 | an `Option<i32>`</span> | |
199 | ||
200 | #### Matching `Some(T)` | |
201 | ||
202 | Let’s examine the first execution of `plus_one` in more detail. When we call | |
203 | `plus_one(five)`, the variable `x` in the body of `plus_one` will have the | |
204 | value `Some(5)`. We then compare that against each match arm. | |
205 | ||
206 | ```rust,ignore | |
207 | None => None, | |
208 | ``` | |
209 | ||
210 | The `Some(5)` value doesn’t match the pattern `None`, so we continue to the | |
211 | next arm. | |
212 | ||
213 | ```rust,ignore | |
214 | Some(i) => Some(i + 1), | |
215 | ``` | |
216 | ||
3b2f2976 | 217 | Does `Some(5)` match `Some(i)`? Well yes it does! We have the same variant. |
cc61c64b XL |
218 | The `i` binds to the value contained in `Some`, so `i` takes the value `5`. The |
219 | code in the match arm is then executed, so we add one to the value of `i` and | |
220 | create a new `Some` value with our total `6` inside. | |
221 | ||
222 | #### Matching `None` | |
223 | ||
224 | Now let’s consider the second call of `plus_one` in Listing 6-5 where `x` is | |
225 | `None`. We enter the `match` and compare to the first arm. | |
226 | ||
227 | ```rust,ignore | |
228 | None => None, | |
229 | ``` | |
230 | ||
231 | It matches! There’s no value to add to, so the program stops and returns the | |
232 | `None` value on the right side of `=>`. Because the first arm matched, no other | |
233 | arms are compared. | |
234 | ||
235 | Combining `match` and enums is useful in many situations. You’ll see this | |
236 | pattern a lot in Rust code: `match` against an enum, bind a variable to the | |
237 | data inside, and then execute code based on it. It’s a bit tricky at first, but | |
238 | once you get used to it, you’ll wish you had it in all languages. It’s | |
239 | consistently a user favorite. | |
240 | ||
241 | ### Matches Are Exhaustive | |
242 | ||
243 | There’s one other aspect of `match` we need to discuss. Consider this version | |
244 | of our `plus_one` function: | |
245 | ||
246 | ```rust,ignore | |
247 | fn plus_one(x: Option<i32>) -> Option<i32> { | |
248 | match x { | |
249 | Some(i) => Some(i + 1), | |
250 | } | |
251 | } | |
252 | ``` | |
253 | ||
254 | We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s | |
255 | a bug Rust knows how to catch. If we try to compile this code, we’ll get this | |
256 | error: | |
257 | ||
258 | ```text | |
259 | error[E0004]: non-exhaustive patterns: `None` not covered | |
260 | --> | |
261 | | | |
262 | 6 | match x { | |
263 | | ^ pattern `None` not covered | |
264 | ``` | |
265 | ||
266 | Rust knows that we didn’t cover every possible case and even knows which | |
267 | pattern we forgot! Matches in Rust are *exhaustive*: we must exhaust every last | |
268 | possibility in order for the code to be valid. Especially in the case of | |
269 | `Option<T>`, when Rust prevents us from forgetting to explicitly handle the | |
270 | `None` case, it protects us from assuming that we have a value when we might | |
271 | have null, thus making the billion dollar mistake discussed earlier. | |
272 | ||
273 | ### The `_` Placeholder | |
274 | ||
275 | Rust also has a pattern we can use in situations when we don’t want to list all | |
276 | possible values. For example, a `u8` can have valid values of 0 through 255. If | |
277 | we only care about the values 1, 3, 5, and 7, we don’t want to have to list out | |
278 | 0, 2, 4, 6, 8, 9 all the way up to 255. Fortunately, we don’t have to: we can | |
279 | use the special pattern `_` instead: | |
280 | ||
281 | ```rust | |
282 | let some_u8_value = 0u8; | |
283 | match some_u8_value { | |
284 | 1 => println!("one"), | |
285 | 3 => println!("three"), | |
286 | 5 => println!("five"), | |
287 | 7 => println!("seven"), | |
288 | _ => (), | |
289 | } | |
290 | ``` | |
291 | ||
292 | The `_` pattern will match any value. By putting it after our other arms, the | |
293 | `_` will match all the possible cases that aren’t specified before it. The `()` | |
294 | is just the unit value, so nothing will happen in the `_` case. As a result, we | |
295 | can say that we want to do nothing for all the possible values that we don’t | |
296 | list before the `_` placeholder. | |
297 | ||
298 | However, the `match` expression can be a bit wordy in a situation in which we | |
299 | only care about *one* of the cases. For this situation, Rust provides `if let`. |