2015-01-08 12:27:03 -06:00
|
|
|
|
% Match
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 14:32:26 -05:00
|
|
|
|
Often, a simple [`if`][if]/`else` isn’t enough, because you have more than two
|
|
|
|
|
possible options. Also, conditions can get quite complex. Rust
|
|
|
|
|
has a keyword, `match`, that allows you to replace complicated `if`/`else`
|
2014-12-02 08:20:48 -06:00
|
|
|
|
groupings with something more powerful. Check it out:
|
|
|
|
|
|
2015-04-10 11:03:30 -05:00
|
|
|
|
```rust
|
2014-12-02 08:20:48 -06:00
|
|
|
|
let x = 5;
|
|
|
|
|
|
|
|
|
|
match x {
|
|
|
|
|
1 => println!("one"),
|
|
|
|
|
2 => println!("two"),
|
|
|
|
|
3 => println!("three"),
|
|
|
|
|
4 => println!("four"),
|
|
|
|
|
5 => println!("five"),
|
|
|
|
|
_ => println!("something else"),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2015-04-18 14:32:26 -05:00
|
|
|
|
[if]: if.html
|
|
|
|
|
|
|
|
|
|
`match` takes an expression and then branches based on its value. Each ‘arm’ of
|
2015-04-10 11:03:30 -05:00
|
|
|
|
the branch is of the form `val => expression`. When the value matches, that arm’s
|
|
|
|
|
expression will be evaluated. It’s called `match` because of the term ‘pattern
|
|
|
|
|
matching’, which `match` is an implementation of. There’s an [entire section on
|
2015-04-18 14:32:26 -05:00
|
|
|
|
patterns][patterns] that covers all the patterns that are possible here.
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-10 11:03:30 -05:00
|
|
|
|
[patterns]: patterns.html
|
|
|
|
|
|
2015-04-18 14:32:26 -05:00
|
|
|
|
So what’s the big advantage? Well, there are a few. First of all, `match`
|
|
|
|
|
enforces ‘exhaustiveness checking’. Do you see that last arm, the one with the
|
2014-12-02 08:20:48 -06:00
|
|
|
|
underscore (`_`)? If we remove that arm, Rust will give us an error:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
error: non-exhaustive patterns: `_` not covered
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In other words, Rust is trying to tell us we forgot a value. Because `x` is an
|
2015-04-18 14:32:26 -05:00
|
|
|
|
integer, Rust knows that it can have a number of different values – for
|
|
|
|
|
example, `6`. Without the `_`, however, there is no arm that could match, and
|
|
|
|
|
so Rust refuses to compile the code. `_` acts like a ‘catch-all arm’. If none
|
|
|
|
|
of the other arms match, the arm with `_` will, and since we have this
|
|
|
|
|
catch-all arm, we now have an arm for every possible value of `x`, and so our
|
|
|
|
|
program will compile successfully.
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
|
|
|
|
`match` is also an expression, which means we can use it on the right-hand
|
2015-04-10 11:03:30 -05:00
|
|
|
|
side of a `let` binding or directly where an expression is used:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-10 11:03:30 -05:00
|
|
|
|
```rust
|
|
|
|
|
let x = 5;
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-05-10 00:10:34 -05:00
|
|
|
|
let number = match x {
|
2015-04-10 11:03:30 -05:00
|
|
|
|
1 => "one",
|
|
|
|
|
2 => "two",
|
|
|
|
|
3 => "three",
|
|
|
|
|
4 => "four",
|
|
|
|
|
5 => "five",
|
|
|
|
|
_ => "something else",
|
|
|
|
|
};
|
2014-12-02 08:20:48 -06:00
|
|
|
|
```
|
|
|
|
|
|
2015-04-18 14:32:26 -05:00
|
|
|
|
Sometimes it’s a nice way of converting something from one type to another.
|
2015-05-12 19:14:26 -05:00
|
|
|
|
|
|
|
|
|
# Matching on enums
|
|
|
|
|
|
|
|
|
|
Another important use of the `match` keyword is to process the possible
|
|
|
|
|
variants of an enum:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
enum Message {
|
|
|
|
|
Quit,
|
|
|
|
|
ChangeColor(i32, i32, i32),
|
|
|
|
|
Move { x: i32, y: i32 },
|
|
|
|
|
Write(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn quit() { /* ... */ }
|
|
|
|
|
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
|
|
|
|
|
fn move_cursor(x: i32, y: i32) { /* ... */ }
|
|
|
|
|
|
|
|
|
|
fn process_message(msg: Message) {
|
|
|
|
|
match msg {
|
|
|
|
|
Message::Quit => quit(),
|
|
|
|
|
Message::ChangeColor(r, g, b) => change_color(r, g, b),
|
|
|
|
|
Message::Move { x: x, y: y } => move_cursor(x, y),
|
|
|
|
|
Message::Write(s) => println!("{}", s),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Again, the Rust compiler checks exhaustiveness, so it demands that you
|
|
|
|
|
have a match arm for every variant of the enum. If you leave one off, it
|
|
|
|
|
will give you a compile-time error unless you use `_`.
|
|
|
|
|
|
|
|
|
|
Unlike the previous uses of `match`, you can’t use the normal `if`
|
|
|
|
|
statement to do this. You can use the [`if let`][if-let] statement,
|
|
|
|
|
which can be seen as an abbreviated form of `match`.
|
|
|
|
|
|
2015-05-14 13:43:50 -05:00
|
|
|
|
[if-let]: if-let.html
|