]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | |
2 | [TOC] | |
3 | ||
4 | # Enums and Pattern Matching | |
5 | ||
6 | In this chapter we’ll look at *enumerations*, also referred to as *enums*. | |
7 | Enums allow you to define a type by enumerating its possible values. First, | |
8 | we’ll define and use an enum to show how an enum can encode meaning along with | |
9 | data. Next, we’ll explore a particularly useful enum, called `Option`, which | |
10 | expresses that a value can be either something or nothing. Then we’ll look at | |
11 | how pattern matching in the `match` expression makes it easy to run different | |
12 | code for different values of an enum. Finally, we’ll cover how the `if let` | |
13 | construct is another convenient and concise idiom available to you to handle | |
14 | enums in your code. | |
15 | ||
16 | Enums are a feature in many languages, but their capabilities differ in each | |
17 | language. Rust’s enums are most similar to *algebraic data types* in functional | |
18 | languages like F#, OCaml, and Haskell. | |
19 | ||
20 | ## Defining an Enum | |
21 | ||
22 | Let’s look at a situation we might want to express in code and see why enums | |
23 | are useful and more appropriate than structs in this case. Say we need to work | |
24 | with IP addresses. Currently, two major standards are used for IP addresses: | |
25 | version four and version six. These are the only possibilities for an IP | |
26 | address that our program will come across: we can *enumerate* all possible | |
27 | values, which is where enumeration gets its name. | |
28 | ||
29 | Any IP address can be either a version four or a version six address but not | |
30 | both at the same time. That property of IP addresses makes the enum data | |
31 | structure appropriate for this case, because enum values can only be one of the | |
32 | variants. Both version four and version six addresses are still fundamentally | |
33 | IP addresses, so they should be treated as the same type when the code is | |
34 | handling situations that apply to any kind of IP address. | |
35 | ||
36 | We can express this concept in code by defining an `IpAddrKind` enumeration and | |
37 | listing the possible kinds an IP address can be, `V4` and `V6`. These are known | |
38 | as the *variants* of the enum: | |
39 | ||
abe05a73 | 40 | ``` |
cc61c64b XL |
41 | enum IpAddrKind { |
42 | V4, | |
43 | V6, | |
44 | } | |
45 | ``` | |
46 | ||
47 | `IpAddrKind` is now a custom data type that we can use elsewhere in our code. | |
48 | ||
49 | ### Enum Values | |
50 | ||
51 | We can create instances of each of the two variants of `IpAddrKind` like this: | |
52 | ||
abe05a73 | 53 | ``` |
cc61c64b XL |
54 | let four = IpAddrKind::V4; |
55 | let six = IpAddrKind::V6; | |
56 | ``` | |
57 | ||
58 | Note that the variants of the enum are namespaced under its identifier, and we | |
59 | use a double colon to separate the two. The reason this is useful is that now | |
60 | both values `IpAddrKind::V4` and `IpAddrKind::V6` are of the same type: | |
61 | `IpAddrKind`. We can then, for instance, define a function that takes any | |
62 | `IpAddrKind`: | |
63 | ||
abe05a73 | 64 | ``` |
cc61c64b XL |
65 | fn route(ip_type: IpAddrKind) { } |
66 | ``` | |
67 | ||
68 | And we can call this function with either variant: | |
69 | ||
abe05a73 | 70 | ``` |
cc61c64b XL |
71 | route(IpAddrKind::V4); |
72 | route(IpAddrKind::V6); | |
73 | ``` | |
74 | ||
75 | Using enums has even more advantages. Thinking more about our IP address type, | |
76 | at the moment we don’t have a way to store the actual IP address *data*; we | |
77 | only know what *kind* it is. Given that you just learned about structs in | |
78 | Chapter 5, you might tackle this problem as shown in Listing 6-1: | |
79 | ||
abe05a73 | 80 | ``` |
cc61c64b XL |
81 | enum IpAddrKind { |
82 | V4, | |
83 | V6, | |
84 | } | |
85 | ||
86 | struct IpAddr { | |
87 | kind: IpAddrKind, | |
88 | address: String, | |
89 | } | |
90 | ||
91 | let home = IpAddr { | |
92 | kind: IpAddrKind::V4, | |
93 | address: String::from("127.0.0.1"), | |
94 | }; | |
95 | ||
96 | let loopback = IpAddr { | |
97 | kind: IpAddrKind::V6, | |
98 | address: String::from("::1"), | |
99 | }; | |
100 | ``` | |
101 | ||
cc61c64b XL |
102 | Listing 6-1: Storing the data and `IpAddrKind` variant of an IP address using a |
103 | `struct` | |
cc61c64b XL |
104 | |
105 | Here, we’ve defined a struct `IpAddr` that has two fields: a `kind` field that | |
106 | is of type `IpAddrKind` (the enum we defined previously) and an `address` field | |
107 | of type `String`. We have two instances of this struct. The first, `home`, has | |
108 | the value `IpAddrKind::V4` as its `kind` with associated address data of | |
109 | `127.0.0.1`. The second instance, `loopback`, has the other variant of | |
110 | `IpAddrKind` as its `kind` value, `V6`, and has address `::1` associated with | |
111 | it. We’ve used a struct to bundle the `kind` and `address` values together, so | |
112 | now the variant is associated with the value. | |
113 | ||
abe05a73 XL |
114 | We can represent the same concept in a more concise way using just an enum, |
115 | rather than an enum inside a struct, by putting data directly into each enum | |
cc61c64b XL |
116 | variant. This new definition of the `IpAddr` enum says that both `V4` and `V6` |
117 | variants will have associated `String` values: | |
118 | ||
abe05a73 | 119 | ``` |
cc61c64b XL |
120 | enum IpAddr { |
121 | V4(String), | |
122 | V6(String), | |
123 | } | |
124 | ||
125 | let home = IpAddr::V4(String::from("127.0.0.1")); | |
126 | ||
127 | let loopback = IpAddr::V6(String::from("::1")); | |
128 | ``` | |
129 | ||
130 | We attach data to each variant of the enum directly, so there is no need for an | |
131 | extra struct. | |
132 | ||
133 | There’s another advantage to using an enum rather than a struct: each variant | |
134 | can have different types and amounts of associated data. Version four type IP | |
135 | addresses will always have four numeric components that will have values | |
136 | between 0 and 255. If we wanted to store `V4` addresses as four `u8` values but | |
137 | still express `V6` addresses as one `String` value, we wouldn’t be able to with | |
138 | a struct. Enums handle this case with ease: | |
139 | ||
abe05a73 | 140 | ``` |
cc61c64b XL |
141 | enum IpAddr { |
142 | V4(u8, u8, u8, u8), | |
143 | V6(String), | |
144 | } | |
145 | ||
146 | let home = IpAddr::V4(127, 0, 0, 1); | |
147 | ||
148 | let loopback = IpAddr::V6(String::from("::1")); | |
149 | ``` | |
150 | ||
151 | We’ve shown several different possibilities that we could define in our code | |
152 | for storing IP addresses of the two different varieties using an enum. However, | |
153 | as it turns out, wanting to store IP addresses and encode which kind they are | |
154 | is so common that the standard library has a definition we can use! Let’s look | |
155 | at how the standard library defines `IpAddr`: it has the exact enum and | |
156 | variants that we’ve defined and used, but it embeds the address data inside the | |
157 | variants in the form of two different structs, which are defined differently | |
158 | for each variant: | |
159 | ||
abe05a73 | 160 | ``` |
cc61c64b XL |
161 | struct Ipv4Addr { |
162 | // details elided | |
163 | } | |
164 | ||
165 | struct Ipv6Addr { | |
166 | // details elided | |
167 | } | |
168 | ||
169 | enum IpAddr { | |
170 | V4(Ipv4Addr), | |
171 | V6(Ipv6Addr), | |
172 | } | |
173 | ``` | |
174 | ||
175 | This code illustrates that you can put any kind of data inside an enum variant: | |
176 | strings, numeric types, or structs, for example. You can even include another | |
177 | enum! Also, standard library types are often not much more complicated than | |
178 | what you might come up with. | |
179 | ||
180 | Note that even though the standard library contains a definition for `IpAddr`, | |
181 | we can still create and use our own definition without conflict because we | |
182 | haven’t brought the standard library’s definition into our scope. We’ll talk | |
abe05a73 | 183 | more about bringing types into scope in Chapter 7. |
cc61c64b XL |
184 | |
185 | Let’s look at another example of an enum in Listing 6-2: this one has a wide | |
186 | variety of types embedded in its variants: | |
187 | ||
abe05a73 | 188 | ``` |
cc61c64b XL |
189 | enum Message { |
190 | Quit, | |
191 | Move { x: i32, y: i32 }, | |
192 | Write(String), | |
193 | ChangeColor(i32, i32, i32), | |
194 | } | |
195 | ``` | |
196 | ||
cc61c64b XL |
197 | Listing 6-2: A `Message` enum whose variants each store different amounts and |
198 | types of values | |
cc61c64b XL |
199 | |
200 | This enum has four variants with different types: | |
201 | ||
202 | * `Quit` has no data associated with it at all. | |
203 | * `Move` includes an anonymous struct inside it. | |
204 | * `Write` includes a single `String`. | |
abe05a73 | 205 | * `ChangeColor` includes three `i32` values. |
cc61c64b XL |
206 | |
207 | Defining an enum with variants like the ones in Listing 6-2 is similar to | |
208 | defining different kinds of struct definitions except the enum doesn’t use the | |
209 | `struct` keyword and all the variants are grouped together under the `Message` | |
210 | type. The following structs could hold the same data that the preceding enum | |
211 | variants hold: | |
212 | ||
abe05a73 | 213 | ``` |
cc61c64b XL |
214 | struct QuitMessage; // unit struct |
215 | struct MoveMessage { | |
216 | x: i32, | |
217 | y: i32, | |
218 | } | |
219 | struct WriteMessage(String); // tuple struct | |
220 | struct ChangeColorMessage(i32, i32, i32); // tuple struct | |
221 | ``` | |
222 | ||
223 | But if we used the different structs, which each have their own type, we | |
224 | wouldn’t be able to as easily define a function that could take any of these | |
225 | kinds of messages as we could with the `Message` enum defined in Listing 6-2, | |
226 | which is a single type. | |
227 | ||
228 | There is one more similarity between enums and structs: just as we’re able to | |
229 | define methods on structs using `impl`, we’re also able to define methods on | |
230 | enums. Here’s a method named `call` that we could define on our `Message` enum: | |
231 | ||
abe05a73 | 232 | ``` |
cc61c64b XL |
233 | impl Message { |
234 | fn call(&self) { | |
235 | // method body would be defined here | |
236 | } | |
237 | } | |
238 | ||
239 | let m = Message::Write(String::from("hello")); | |
240 | m.call(); | |
241 | ``` | |
242 | ||
243 | The body of the method would use `self` to get the value that we called the | |
244 | method on. In this example, we’ve created a variable `m` that has the value | |
abe05a73 XL |
245 | `Message::Write(String::from("hello"))`, and that is what `self` will be in the |
246 | body of the `call` method when `m.call()` runs. | |
cc61c64b XL |
247 | |
248 | Let’s look at another enum in the standard library that is very common and | |
249 | useful: `Option`. | |
250 | ||
251 | ### The `Option` Enum and Its Advantages Over Null Values | |
252 | ||
253 | In the previous section, we looked at how the `IpAddr` enum let us use Rust’s | |
254 | type system to encode more information than just the data into our program. | |
255 | This section explores a case study of `Option`, which is another enum defined | |
256 | by the standard library. The `Option` type is used in many places because it | |
257 | encodes the very common scenario in which a value could be something or it | |
258 | could be nothing. Expressing this concept in terms of the type system means the | |
259 | compiler can check that you’ve handled all the cases you should be handling, | |
260 | which can prevent bugs that are extremely common in other programming languages. | |
261 | ||
262 | Programming language design is often thought of in terms of which features you | |
263 | include, but the features you exclude are important too. Rust doesn’t have the | |
264 | null feature that many other languages have. *Null* is a value that means there | |
265 | is no value there. In languages with null, variables can always be in one of | |
266 | two states: null or not-null. | |
267 | ||
268 | In “Null References: The Billion Dollar Mistake,” Tony Hoare, the inventor of | |
269 | null, has this to say: | |
270 | ||
271 | > I call it my billion-dollar mistake. At that time, I was designing the first | |
272 | > comprehensive type system for references in an object-oriented language. My | |
273 | > goal was to ensure that all use of references should be absolutely safe, with | |
3b2f2976 | 274 | > checking performed automatically by the compiler. But I couldn’t resist the |
cc61c64b XL |
275 | > temptation to put in a null reference, simply because it was so easy to |
276 | > implement. This has led to innumerable errors, vulnerabilities, and system | |
277 | > crashes, which have probably caused a billion dollars of pain and damage in | |
278 | > the last forty years. | |
279 | ||
280 | The problem with null values is that if you try to actually use a value that’s | |
281 | null as if it is a not-null value, you’ll get an error of some kind. Because | |
282 | this null or not-null property is pervasive, it’s extremely easy to make this | |
283 | kind of error. | |
284 | ||
285 | However, the concept that null is trying to express is still a useful one: a | |
286 | null is a value that is currently invalid or absent for some reason. | |
287 | ||
288 | The problem isn’t with the actual concept but with the particular | |
289 | implementation. As such, Rust does not have nulls, but it does have an enum | |
290 | that can encode the concept of a value being present or absent. This enum is | |
291 | `Option<T>`, and it is defined by the standard library as follows: | |
292 | ||
abe05a73 | 293 | ``` |
cc61c64b XL |
294 | enum Option<T> { |
295 | Some(T), | |
296 | None, | |
297 | } | |
298 | ``` | |
299 | ||
300 | The `Option<T>` enum is so useful that it’s even included in the prelude; you | |
abe05a73 XL |
301 | don’t need to bring it into scope explicitly. In addition, so are its variants: |
302 | you can use `Some` and `None` directly without prefixing them with `Option::`. | |
cc61c64b XL |
303 | `Option<T>` is still just a regular enum, and `Some(T)` and `None` are still |
304 | variants of type `Option<T>`. | |
305 | ||
306 | The `<T>` syntax is a feature of Rust we haven’t talked about yet. It’s a | |
307 | generic type parameter, and we’ll cover generics in more detail in Chapter 10. | |
308 | For now, all you need to know is that `<T>` means the `Some` variant of the | |
309 | `Option` enum can hold one piece of data of any type. Here are some examples of | |
310 | using `Option` values to hold number types and string types: | |
311 | ||
abe05a73 | 312 | ``` |
cc61c64b XL |
313 | let some_number = Some(5); |
314 | let some_string = Some("a string"); | |
315 | ||
316 | let absent_number: Option<i32> = None; | |
317 | ``` | |
318 | ||
319 | If we use `None` rather than `Some`, we need to tell Rust what type of | |
3b2f2976 | 320 | `Option<T>` we have, because the compiler can’t infer the type that the `Some` |
cc61c64b XL |
321 | variant will hold by looking only at a `None` value. |
322 | ||
323 | When we have a `Some` value, we know that a value is present, and the value is | |
324 | held within the `Some`. When we have a `None` value, in some sense, it means | |
325 | the same thing as null: we don’t have a valid value. So why is having | |
326 | `Option<T>` any better than having null? | |
327 | ||
328 | In short, because `Option<T>` and `T` (where `T` can be any type) are different | |
329 | types, the compiler won’t let us use an `Option<T>` value as if it was | |
330 | definitely a valid value. For example, this code won’t compile because it’s | |
abe05a73 | 331 | trying to add an `i8` to an `Option<i8>`: |
cc61c64b | 332 | |
abe05a73 | 333 | ``` |
cc61c64b XL |
334 | let x: i8 = 5; |
335 | let y: Option<i8> = Some(5); | |
336 | ||
337 | let sum = x + y; | |
338 | ``` | |
339 | ||
340 | If we run this code, we get an error message like this: | |
341 | ||
abe05a73 | 342 | ``` |
cc61c64b XL |
343 | error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is |
344 | not satisfied | |
345 | --> | |
346 | | | |
abe05a73 XL |
347 | 5 | let sum = x + y; |
348 | | ^ no implementation for `i8 + std::option::Option<i8>` | |
cc61c64b XL |
349 | | |
350 | ``` | |
351 | ||
352 | Intense! In effect, this error message means that Rust doesn’t understand how | |
abe05a73 | 353 | to add an `i8` and an `Option<i8>`, because they’re different types. When we |
cc61c64b XL |
354 | have a value of a type like `i8` in Rust, the compiler will ensure that we |
355 | always have a valid value. We can proceed confidently without having to check | |
356 | for null before using that value. Only when we have an `Option<i8>` (or | |
357 | whatever type of value we’re working with) do we have to worry about possibly | |
358 | not having a value, and the compiler will make sure we handle that case before | |
359 | using the value. | |
360 | ||
361 | In other words, you have to convert an `Option<T>` to a `T` before you can | |
362 | perform `T` operations with it. Generally, this helps catch one of the most | |
363 | common issues with null: assuming that something isn’t null when it actually | |
364 | is. | |
365 | ||
366 | Not having to worry about missing an assumption of having a not-null value | |
367 | helps you to be more confident in your code. In order to have a value that can | |
368 | possibly be null, you must explicitly opt in by making the type of that value | |
369 | `Option<T>`. Then, when you use that value, you are required to explicitly | |
370 | handle the case when the value is null. Everywhere that a value has a type that | |
371 | isn’t an `Option<T>`, you *can* safely assume that the value isn’t null. This | |
372 | was a deliberate design decision for Rust to limit null’s pervasiveness and | |
373 | increase the safety of Rust code. | |
374 | ||
375 | So, how do you get the `T` value out of a `Some` variant when you have a value | |
376 | of type `Option<T>` so you can use that value? The `Option<T>` enum has a large | |
377 | number of methods that are useful in a variety of situations; you can check | |
378 | them out in its documentation. Becoming familiar with the methods on | |
379 | `Option<T>` will be extremely useful in your journey with Rust. | |
380 | ||
381 | In general, in order to use an `Option<T>` value, we want to have code that | |
382 | will handle each variant. We want some code that will run only when we have a | |
383 | `Some(T)` value, and this code is allowed to use the inner `T`. We want some | |
384 | other code to run if we have a `None` value, and that code doesn’t have a `T` | |
385 | value available. The `match` expression is a control flow construct that does | |
386 | just this when used with enums: it will run different code depending on which | |
387 | variant of the enum it has, and that code can use the data inside the matching | |
388 | value. | |
389 | ||
390 | ## The `match` Control Flow Operator | |
391 | ||
392 | Rust has an extremely powerful control-flow operator called `match` that allows | |
393 | us to compare a value against a series of patterns and then execute code based | |
394 | on which pattern matches. Patterns can be made up of literal values, variable | |
abe05a73 XL |
395 | names, wildcards, and many other things; Chapter 18 covers all the different |
396 | kinds of patterns and what they do. The power of `match` comes from the | |
397 | expressiveness of the patterns and the compiler checks that make sure all | |
cc61c64b XL |
398 | possible cases are handled. |
399 | ||
400 | Think of a `match` expression kind of like a coin sorting machine: coins slide | |
401 | down a track with variously sized holes along it, and each coin falls through | |
402 | the first hole it encounters that it fits into. In the same way, values go | |
403 | through each pattern in a `match`, and at the first pattern the value “fits,” | |
404 | the value will fall into the associated code block to be used during execution. | |
405 | ||
406 | Because we just mentioned coins, let’s use them as an example using `match`! We | |
407 | can write a function that can take an unknown United States coin and, in a | |
408 | similar way as the counting machine, determine which coin it is and return its | |
409 | value in cents, as shown here in Listing 6-3: | |
410 | ||
abe05a73 | 411 | ``` |
cc61c64b XL |
412 | enum Coin { |
413 | Penny, | |
414 | Nickel, | |
415 | Dime, | |
416 | Quarter, | |
417 | } | |
418 | ||
abe05a73 | 419 | fn value_in_cents(coin: Coin) -> u32 { |
cc61c64b XL |
420 | match coin { |
421 | Coin::Penny => 1, | |
422 | Coin::Nickel => 5, | |
423 | Coin::Dime => 10, | |
424 | Coin::Quarter => 25, | |
425 | } | |
426 | } | |
427 | ``` | |
428 | ||
cc61c64b XL |
429 | Listing 6-3: An enum and a `match` expression that has the variants of the enum |
430 | as its patterns. | |
cc61c64b XL |
431 | |
432 | Let’s break down the `match` in the `value_in_cents` function. First, we list | |
433 | the `match` keyword followed by an expression, which in this case is the value | |
434 | `coin`. This seems very similar to an expression used with `if`, but there’s a | |
435 | big difference: with `if`, the expression needs to return a boolean value. | |
436 | Here, it can be any type. The type of `coin` in this example is the `Coin` enum | |
437 | that we defined in Listing 6-3. | |
438 | ||
439 | Next are the `match` arms. An arm has two parts: a pattern and some code. The | |
440 | first arm here has a pattern that is the value `Coin::Penny` and then the `=>` | |
441 | operator that separates the pattern and the code to run. The code in this case | |
442 | is just the value `1`. Each arm is separated from the next with a comma. | |
443 | ||
444 | When the `match` expression executes, it compares the resulting value against | |
445 | the pattern of each arm, in order. If a pattern matches the value, the code | |
446 | associated with that pattern is executed. If that pattern doesn’t match the | |
447 | value, execution continues to the next arm, much like a coin sorting machine. | |
448 | We can have as many arms as we need: in Listing 6-3, our `match` has four arms. | |
449 | ||
450 | The code associated with each arm is an expression, and the resulting value of | |
451 | the expression in the matching arm is the value that gets returned for the | |
452 | entire `match` expression. | |
453 | ||
abe05a73 XL |
454 | Curly brackets typically aren’t used if the match arm code is short, as it is |
455 | in Listing 6-3 where each arm just returns a value. If you want to run multiple | |
456 | lines of code in a match arm, you can use curly brackets. For example, the | |
cc61c64b XL |
457 | following code would print out “Lucky penny!” every time the method was called |
458 | with a `Coin::Penny` but would still return the last value of the block, `1`: | |
459 | ||
abe05a73 XL |
460 | ``` |
461 | fn value_in_cents(coin: Coin) -> u32 { | |
cc61c64b XL |
462 | match coin { |
463 | Coin::Penny => { | |
464 | println!("Lucky penny!"); | |
465 | 1 | |
466 | }, | |
467 | Coin::Nickel => 5, | |
468 | Coin::Dime => 10, | |
469 | Coin::Quarter => 25, | |
470 | } | |
471 | } | |
472 | ``` | |
473 | ||
474 | ### Patterns that Bind to Values | |
475 | ||
476 | Another useful feature of match arms is that they can bind to parts of the | |
477 | values that match the pattern. This is how we can extract values out of enum | |
478 | variants. | |
479 | ||
480 | As an example, let’s change one of our enum variants to hold data inside it. | |
abe05a73 | 481 | From 1999 through 2008, the United States minted quarters with different |
cc61c64b XL |
482 | designs for each of the 50 states on one side. No other coins got state |
483 | designs, so only quarters have this extra value. We can add this information to | |
abe05a73 | 484 | our `enum` by changing the `Quarter` variant to include a `UsState` value stored |
3b2f2976 | 485 | inside it, which we’ve done here in Listing 6-4: |
cc61c64b | 486 | |
abe05a73 | 487 | ``` |
cc61c64b XL |
488 | #[derive(Debug)] // So we can inspect the state in a minute |
489 | enum UsState { | |
490 | Alabama, | |
491 | Alaska, | |
492 | // ... etc | |
493 | } | |
494 | ||
495 | enum Coin { | |
496 | Penny, | |
497 | Nickel, | |
498 | Dime, | |
499 | Quarter(UsState), | |
500 | } | |
501 | ``` | |
502 | ||
cc61c64b XL |
503 | Listing 6-4: A `Coin` enum where the `Quarter` variant also holds a `UsState` |
504 | value | |
cc61c64b XL |
505 | |
506 | Let’s imagine that a friend of ours is trying to collect all 50 state quarters. | |
507 | While we sort our loose change by coin type, we’ll also call out the name of | |
508 | the state associated with each quarter so if it’s one our friend doesn’t have, | |
509 | they can add it to their collection. | |
510 | ||
511 | In the match expression for this code, we add a variable called `state` to the | |
512 | pattern that matches values of the variant `Coin::Quarter`. When a | |
513 | `Coin::Quarter` matches, the `state` variable will bind to the value of that | |
514 | quarter’s state. Then we can use `state` in the code for that arm, like so: | |
515 | ||
abe05a73 XL |
516 | ``` |
517 | fn value_in_cents(coin: Coin) -> u32 { | |
cc61c64b XL |
518 | match coin { |
519 | Coin::Penny => 1, | |
520 | Coin::Nickel => 5, | |
521 | Coin::Dime => 10, | |
522 | Coin::Quarter(state) => { | |
523 | println!("State quarter from {:?}!", state); | |
524 | 25 | |
525 | }, | |
526 | } | |
527 | } | |
528 | ``` | |
529 | ||
530 | If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` | |
531 | would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each | |
532 | of the match arms, none of them match until we reach `Coin::Quarter(state)`. At | |
533 | that point, the binding for `state` will be the value `UsState::Alaska`. We can | |
534 | then use that binding in the `println!` expression, thus getting the inner | |
535 | state value out of the `Coin` enum variant for `Quarter`. | |
536 | ||
537 | ### Matching with `Option<T>` | |
538 | ||
539 | In the previous section we wanted to get the inner `T` value out of the `Some` | |
540 | case when using `Option<T>`; we can also handle `Option<T>` using `match` as we | |
541 | did with the `Coin` enum! Instead of comparing coins, we’ll compare the | |
542 | variants of `Option<T>`, but the way that the `match` expression works remains | |
543 | the same. | |
544 | ||
545 | Let’s say we want to write a function that takes an `Option<i32>`, and if | |
546 | there’s a value inside, adds one to that value. If there isn’t a value inside, | |
547 | the function should return the `None` value and not attempt to perform any | |
548 | operations. | |
549 | ||
550 | This function is very easy to write, thanks to `match`, and will look like | |
551 | Listing 6-5: | |
552 | ||
abe05a73 | 553 | ``` |
cc61c64b XL |
554 | fn plus_one(x: Option<i32>) -> Option<i32> { |
555 | match x { | |
556 | None => None, | |
557 | Some(i) => Some(i + 1), | |
558 | } | |
559 | } | |
560 | ||
561 | let five = Some(5); | |
562 | let six = plus_one(five); | |
563 | let none = plus_one(None); | |
564 | ``` | |
565 | ||
cc61c64b | 566 | Listing 6-5: A function that uses a `match` expression on an `Option<i32>` |
cc61c64b XL |
567 | |
568 | #### Matching `Some(T)` | |
569 | ||
570 | Let’s examine the first execution of `plus_one` in more detail. When we call | |
abe05a73 | 571 | `plus_one(five)`, the variable `x` in the body of `plus_one` will have the |
cc61c64b XL |
572 | value `Some(5)`. We then compare that against each match arm. |
573 | ||
abe05a73 | 574 | ``` |
cc61c64b XL |
575 | None => None, |
576 | ``` | |
577 | ||
abe05a73 | 578 | The `Some(5)` value doesn’t match the pattern `None`, so we continue to the |
cc61c64b XL |
579 | next arm. |
580 | ||
abe05a73 | 581 | ``` |
cc61c64b XL |
582 | Some(i) => Some(i + 1), |
583 | ``` | |
584 | ||
abe05a73 | 585 | Does `Some(5)` match `Some(i)`? Well yes it does! We have the same variant. |
cc61c64b XL |
586 | The `i` binds to the value contained in `Some`, so `i` takes the value `5`. The |
587 | code in the match arm is then executed, so we add one to the value of `i` and | |
588 | create a new `Some` value with our total `6` inside. | |
589 | ||
590 | #### Matching `None` | |
591 | ||
592 | Now let’s consider the second call of `plus_one` in Listing 6-5 where `x` is | |
abe05a73 | 593 | `None`. We enter the `match` and compare to the first arm. |
cc61c64b | 594 | |
abe05a73 | 595 | ``` |
cc61c64b XL |
596 | None => None, |
597 | ``` | |
598 | ||
599 | It matches! There’s no value to add to, so the program stops and returns the | |
600 | `None` value on the right side of `=>`. Because the first arm matched, no other | |
601 | arms are compared. | |
602 | ||
603 | Combining `match` and enums is useful in many situations. You’ll see this | |
604 | pattern a lot in Rust code: `match` against an enum, bind a variable to the | |
605 | data inside, and then execute code based on it. It’s a bit tricky at first, but | |
606 | once you get used to it, you’ll wish you had it in all languages. It’s | |
607 | consistently a user favorite. | |
608 | ||
609 | ### Matches Are Exhaustive | |
610 | ||
611 | There’s one other aspect of `match` we need to discuss. Consider this version | |
612 | of our `plus_one` function: | |
613 | ||
abe05a73 | 614 | ``` |
cc61c64b XL |
615 | fn plus_one(x: Option<i32>) -> Option<i32> { |
616 | match x { | |
617 | Some(i) => Some(i + 1), | |
618 | } | |
619 | } | |
620 | ``` | |
621 | ||
622 | We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s | |
623 | a bug Rust knows how to catch. If we try to compile this code, we’ll get this | |
624 | error: | |
625 | ||
abe05a73 | 626 | ``` |
cc61c64b XL |
627 | error[E0004]: non-exhaustive patterns: `None` not covered |
628 | --> | |
629 | | | |
630 | 6 | match x { | |
631 | | ^ pattern `None` not covered | |
632 | ``` | |
633 | ||
634 | Rust knows that we didn’t cover every possible case and even knows which | |
635 | pattern we forgot! Matches in Rust are *exhaustive*: we must exhaust every last | |
636 | possibility in order for the code to be valid. Especially in the case of | |
637 | `Option<T>`, when Rust prevents us from forgetting to explicitly handle the | |
638 | `None` case, it protects us from assuming that we have a value when we might | |
639 | have null, thus making the billion dollar mistake discussed earlier. | |
640 | ||
641 | ### The `_` Placeholder | |
642 | ||
643 | Rust also has a pattern we can use in situations when we don’t want to list all | |
644 | possible values. For example, a `u8` can have valid values of 0 through 255. If | |
645 | we only care about the values 1, 3, 5, and 7, we don’t want to have to list out | |
646 | 0, 2, 4, 6, 8, 9 all the way up to 255. Fortunately, we don’t have to: we can | |
647 | use the special pattern `_` instead: | |
648 | ||
abe05a73 | 649 | ``` |
cc61c64b XL |
650 | let some_u8_value = 0u8; |
651 | match some_u8_value { | |
652 | 1 => println!("one"), | |
653 | 3 => println!("three"), | |
654 | 5 => println!("five"), | |
655 | 7 => println!("seven"), | |
656 | _ => (), | |
657 | } | |
658 | ``` | |
659 | ||
660 | The `_` pattern will match any value. By putting it after our other arms, the | |
661 | `_` will match all the possible cases that aren’t specified before it. The `()` | |
662 | is just the unit value, so nothing will happen in the `_` case. As a result, we | |
663 | can say that we want to do nothing for all the possible values that we don’t | |
664 | list before the `_` placeholder. | |
665 | ||
666 | However, the `match` expression can be a bit wordy in a situation in which we | |
667 | only care about *one* of the cases. For this situation, Rust provides `if let`. | |
668 | ||
669 | ## Concise Control Flow with `if let` | |
670 | ||
671 | The `if let` syntax lets you combine `if` and `let` into a less verbose way to | |
672 | handle values that match one pattern and ignore the rest. Consider the program | |
673 | in Listing 6-6 that matches on an `Option<u8>` value but only wants to execute | |
674 | code if the value is three: | |
675 | ||
abe05a73 | 676 | ``` |
cc61c64b XL |
677 | let some_u8_value = Some(0u8); |
678 | match some_u8_value { | |
679 | Some(3) => println!("three"), | |
680 | _ => (), | |
681 | } | |
682 | ``` | |
683 | ||
cc61c64b XL |
684 | Listing 6-6: A `match` that only cares about executing code when the value is |
685 | `Some(3)` | |
cc61c64b XL |
686 | |
687 | We want to do something with the `Some(3)` match but do nothing with any other | |
688 | `Some<u8>` value or the `None` value. To satisfy the `match` expression, we | |
689 | have to add `_ => ()` after processing just one variant, which is a lot of | |
690 | boilerplate code to add. | |
691 | ||
692 | Instead, we could write this in a shorter way using `if let`. The following | |
693 | code behaves the same as the `match` in Listing 6-6: | |
694 | ||
abe05a73 | 695 | ``` |
cc61c64b XL |
696 | if let Some(3) = some_u8_value { |
697 | println!("three"); | |
698 | } | |
699 | ``` | |
700 | ||
701 | `if let` takes a pattern and an expression separated by an `=`. It works the | |
702 | same way as a `match`, where the expression is given to the `match` and the | |
703 | pattern is its first arm. | |
704 | ||
705 | Using `if let` means you have less to type, less indentation, and less | |
706 | boilerplate code. However, we’ve lost the exhaustive checking that `match` | |
707 | enforces. Choosing between `match` and `if let` depends on what you’re doing in | |
708 | your particular situation and if gaining conciseness is an appropriate | |
709 | trade-off for losing exhaustive checking. | |
710 | ||
711 | In other words, you can think of `if let` as syntax sugar for a `match` that | |
712 | runs code when the value matches one pattern and then ignores all other values. | |
713 | ||
714 | We can include an `else` with an `if let`. The block of code that goes with the | |
715 | `else` is the same as the block of code that would go with the `_` case in the | |
716 | `match` expression that is equivalent to the `if let` and `else`. Recall the | |
717 | `Coin` enum definition in Listing 6-4, where the `Quarter` variant also held a | |
718 | `UsState` value. If we wanted to count all non-quarter coins we see while also | |
719 | announcing the state of the quarters, we could do that with a `match` | |
720 | expression like this: | |
721 | ||
abe05a73 | 722 | ``` |
cc61c64b XL |
723 | let mut count = 0; |
724 | match coin { | |
725 | Coin::Quarter(state) => println!("State quarter from {:?}!", state), | |
726 | _ => count += 1, | |
727 | } | |
728 | ``` | |
729 | ||
730 | Or we could use an `if let` and `else` expression like this: | |
731 | ||
abe05a73 | 732 | ``` |
cc61c64b XL |
733 | let mut count = 0; |
734 | if let Coin::Quarter(state) = coin { | |
735 | println!("State quarter from {:?}!", state); | |
736 | } else { | |
737 | count += 1; | |
738 | } | |
739 | ``` | |
740 | ||
741 | If you have a situation in which your program has logic that is too verbose to | |
742 | express using a `match`, remember that `if let` is in your Rust toolbox as well. | |
743 | ||
744 | ## Summary | |
745 | ||
746 | We’ve now covered how to use enums to create custom types that can be one of a | |
747 | set of enumerated values. We’ve shown how the standard library’s `Option<T>` | |
748 | type helps you use the type system to prevent errors. When enum values have | |
749 | data inside them, you can use `match` or `if let` to extract and use those | |
750 | values, depending on how many cases you need to handle. | |
751 | ||
752 | Your Rust programs can now express concepts in your domain using structs and | |
753 | enums. Creating custom types to use in your API ensures type safety: the | |
754 | compiler will make certain your functions only get values of the type each | |
755 | function expects. | |
756 | ||
757 | In order to provide a well-organized API to your users that is straightforward | |
758 | to use and only exposes exactly what your users will need, let’s now turn to | |
759 | Rust’s modules. |