Sitemap

Rust Day 12

4 min readFeb 9, 2026

--

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 number
  • None → there is no number

Now suppose I want a function called plus_one:

  • If there is a number inside → add 1 to 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.

Press enter or click to view image in full size

--

--