Rust Day 12
The match control flow Construct 2
Option<T> match pattern
fn main() {
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}Think of Option<T> as Rust’s way of saying: “Hey, this value might exist, or it might not.”
So for Option<i32>, there are only two possibilities:
Some(number)→ there is a numberNone→ there is no number
Now suppose I want a function called plus_one:
- If there is a number inside → add
1to it - If there is no number → don’t do anything, just return
None
Let’s examine the first execution of plus_one in more detail. When we call plus_one(five), the variable x in the body of plus_one will have the value Some(5).
Some(i) => Some(i + 1),
Some(5) => Some(5 + 1),Matches are exhaustive
The arms’ patterns must cover all possibilities. Consider this version of our plus_one function, which has a bug and won’t compile:
NOTE:
If you don’t know what is arms’ pattern : It is simply using “ => ” in match
fn main() {
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
// No None case
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}In above example, we didn’t handle the None case, so this code will cause a bug. Luckily, it’s a bug Rust knows how to catch. If we try to compile this code, we’ll get this error:
$ cargo run
Compiling enums v0.1.0 (file:///projects/enums)
error[E0004]: non-exhaustive patterns: `None` not covered
--> src/main.rs:3:15
|
3 | match x {
| ^ pattern `None` not covered
|Matches in Rust are exhaustive: We must exhaust every last possibility in order for the code to be valid. Especially in the case of Option<T>, when Rust prevents us from forgetting to explicitly handle the None case, it protects us from assuming that we have a value when we might have null, thus making the billion-dollar mistake.
Catch-all Patterns and the _ Placeholder
Imagine we’re implementing a game where,
Get zoolpher’s stories in your inbox
Join Medium for free to get updates from this writer.
if you roll a 3 on a dice roll, your player doesn’t move but instead gets a fancy new hat.
If you roll a 7, your player loses a fancy hat.
For all other values, your player moves that number of spaces on the game board.
Here’s a match that implements that logic, with the result of the dice roll hardcoded rather than a random value, and all other logic represented by functions without bodies because actually implementing them is out of scope for this example:
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other), // This catch-all pattern meets the requirement that match must be exhaustive.
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
}// This catch-all pattern meets the requirement; that match must be exhaustive.
other => move_player(other),Note:
We have to put the catch-all arm last because the patterns are evaluated in order. If we had put the catch-all arm earlier, the other arms would never run, so Rust will warn us if we add arms after a catch-all!
Rust also has a pattern we can use when we want a catch-all but don’t want to use the value in the catch-all pattern: _ is a special pattern that matches any value and does not bind to that value. This tells Rust we aren’t going to use the value, so Rust won’t warn us about an unused variable.
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
}This example also meets the exhaustiveness requirement because we’re explicitly ignoring all other values in the last arm; we haven’t forgotten anything.
“Using _ in match is similar to using default in switch case in C language.”
Finally, we’ll change the rules of the game one more time so that nothing else happens on your turn if you roll anything other than a 3 or a 7.
We can express that by using the unit value (the empty tuple type we mentioned in “The Tuple Type” section) as the code that goes with the _ arm:
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
}Here, we’re telling Rust explicitly that we aren’t going to use any other value that doesn’t match a pattern in an earlier arm, and we don’t want to run any code in this case.




