Often, a simple 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
groupings with something more powerful. Check it out:
# #![allow(unused_variables)] #fn main() { let x = 5; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), 4 => println!("four"), 5 => println!("five"), _ => println!("something else"), } #}
match
takes an expression and then branches based on its value. Each ‘arm’ of 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 a separate section on patterns that covers all the patterns that are possible here.
One of the many advantages of match
is it enforces ‘exhaustiveness checking’. For example if we remove the last arm with the underscore _
, the compiler will give us an error:
error: non-exhaustive patterns: `_` not covered
Rust is telling us that we forgot some value. The compiler infers from x
that it can have any 32bit integer value; for example -2,147,483,648 to 2,147,483,647. The _
acts as a 'catch-all', and will catch all possible values that aren't specified in an arm of match
. As you can see in the previous example, we provide match
arms for integers 1-5, if x
is 6 or any other value, then it is caught by _
.
match
is also an expression, which means we can use it on the right-hand side of a let
binding or directly where an expression is used:
# #![allow(unused_variables)] #fn main() { let x = 5; let number = match x { 1 => "one", 2 => "two", 3 => "three", 4 => "four", 5 => "five", _ => "something else", }; #}
Sometimes it’s a nice way of converting something from one type to another; in this example the integers are converted to String
.
Another important use of the match
keyword is to process the possible variants of an enum:
# #![allow(unused_variables)] #fn main() { 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, y: new_name_for_y } => move_cursor(x, new_name_for_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 _
or provide all possible arms.
Unlike the previous uses of match
, you can’t use the normal if
statement to do this. You can use the if let
statement, which can be seen as an abbreviated form of match
.
© 2010 The Rust Project Developers
Licensed under the Apache License, Version 2.0 or the MIT license, at your option.
https://doc.rust-lang.org/book/first-edition/match.html