]> git.lizzy.rs Git - rust.git/blob - src/doc/trpl/match.md
Rollup merge of #21357 - kimroen:patch-1, r=sanxiyn
[rust.git] / src / doc / trpl / match.md
1 % Match
2
3 Often, a simple `if`/`else` isn't enough, because you have more than two
4 possible options. Also, `else` conditions can get incredibly complicated, so
5 what's the solution?
6
7 Rust has a keyword, `match`, that allows you to replace complicated `if`/`else`
8 groupings with something more powerful. Check it out:
9
10 ```{rust}
11 let x = 5;
12
13 match x {
14     1 => println!("one"),
15     2 => println!("two"),
16     3 => println!("three"),
17     4 => println!("four"),
18     5 => println!("five"),
19     _ => println!("something else"),
20 }
21 ```
22
23 `match` takes an expression and then branches based on its value. Each *arm* of
24 the branch is of the form `val => expression`. When the value matches, that arm's
25 expression will be evaluated. It's called `match` because of the term 'pattern
26 matching', which `match` is an implementation of.
27
28 So what's the big advantage here? Well, there are a few. First of all, `match`
29 enforces *exhaustiveness checking*. Do you see that last arm, the one with the
30 underscore (`_`)? If we remove that arm, Rust will give us an error:
31
32 ```text
33 error: non-exhaustive patterns: `_` not covered
34 ```
35
36 In other words, Rust is trying to tell us we forgot a value. Because `x` is an
37 integer, Rust knows that it can have a number of different values – for example,
38 `6`. Without the `_`, however, there is no arm that could match, and so Rust refuses
39 to compile. `_` acts like a *catch-all arm*. If none of the other arms match,
40 the arm with `_` will, and since we have this catch-all arm, we now have an arm
41 for every possible value of `x`, and so our program will compile successfully.
42
43 `match` statements also destructure enums, as well. Remember this code from the
44 section on enums?
45
46 ```{rust}
47 use std::cmp::Ordering;
48
49 fn cmp(a: i32, b: i32) -> Ordering {
50     if a < b { Ordering::Less }
51     else if a > b { Ordering::Greater }
52     else { Ordering::Equal }
53 }
54
55 fn main() {
56     let x = 5;
57     let y = 10;
58
59     let ordering = cmp(x, y);
60
61     if ordering == Ordering::Less {
62         println!("less");
63     } else if ordering == Ordering::Greater {
64         println!("greater");
65     } else if ordering == Ordering::Equal {
66         println!("equal");
67     }
68 }
69 ```
70
71 We can re-write this as a `match`:
72
73 ```{rust}
74 use std::cmp::Ordering;
75
76 fn cmp(a: i32, b: i32) -> Ordering {
77     if a < b { Ordering::Less }
78     else if a > b { Ordering::Greater }
79     else { Ordering::Equal }
80 }
81
82 fn main() {
83     let x = 5;
84     let y = 10;
85
86     match cmp(x, y) {
87         Ordering::Less => println!("less"),
88         Ordering::Greater => println!("greater"),
89         Ordering::Equal => println!("equal"),
90     }
91 }
92 ```
93
94 This version has way less noise, and it also checks exhaustively to make sure
95 that we have covered all possible variants of `Ordering`. With our `if`/`else`
96 version, if we had forgotten the `Greater` case, for example, our program would
97 have happily compiled. If we forget in the `match`, it will not. Rust helps us
98 make sure to cover all of our bases.
99
100 `match` expressions also allow us to get the values contained in an `enum`
101 (also known as destructuring) as follows:
102
103 ```{rust}
104 enum OptionalInt {
105     Value(i32),
106     Missing,
107 }
108
109 fn main() {
110     let x = OptionalInt::Value(5);
111     let y = OptionalInt::Missing;
112
113     match x {
114         OptionalInt::Value(n) => println!("x is {}", n),
115         OptionalInt::Missing => println!("x is missing!"),
116     }
117
118     match y {
119         OptionalInt::Value(n) => println!("y is {}", n),
120         OptionalInt::Missing => println!("y is missing!"),
121     }
122 }
123 ```
124
125 That is how you can get and use the values contained in `enum`s.
126 It can also allow us to handle errors or unexpected computations; for example, a
127 function that is not guaranteed to be able to compute a result (an `i32` here)
128 could return an `OptionalInt`, and we would handle that value with a `match`.
129 As you can see, `enum` and `match` used together are quite useful!
130
131 `match` is also an expression, which means we can use it on the right-hand
132 side of a `let` binding or directly where an expression is used. We could
133 also implement the previous example like this:
134
135 ```{rust}
136 use std::cmp::Ordering;
137
138 fn cmp(a: i32, b: i32) -> Ordering {
139     if a < b { Ordering::Less }
140     else if a > b { Ordering::Greater }
141     else { Ordering::Equal }
142 }
143
144 fn main() {
145     let x = 5;
146     let y = 10;
147
148     println!("{}", match cmp(x, y) {
149         Ordering::Less => "less",
150         Ordering::Greater => "greater",
151         Ordering::Equal => "equal",
152     });
153 }
154 ```
155
156 Sometimes, it's a nice pattern.