Lesson 10
Pattern Matching
Master match expressions, destructuring, guards, and the if let shorthand.
Match: Rust's Swiss Army Knife
The match expression is one of Rust's most powerful features. It compares a value against a series of patterns and runs the code for the first match. Unlike switch statements in other languages, Rust's match is exhaustive -- you must handle every possible case. The compiler won't let you forget one.
match value {
pattern1 => expression1,
pattern2 => expression2,
_ => default_expression, // _ matches everything else
}
Destructuring
Patterns can pull apart (destructure) enums, tuples, and structs. This lets you bind the inner values to variables right in the pattern. Combined with match, it makes handling complex data structures elegant and safe.
If Let
When you only care about one pattern, if let is a concise shorthand for match. It's perfect for handling Option or checking a single enum variant without writing a full match block.
Try it
enum Command {
Quit,
Echo(String),
Move { x: i32, y: i32 },
Color(u8, u8, u8),
}
fn process_command(cmd: &Command) {
match cmd {
Command::Quit => println!(" Quitting!"),
Command::Echo(msg) => println!(" Echo: {}", msg),
Command::Move { x, y } => println!(" Moving to ({}, {})", x, y),
Command::Color(r, g, b) => println!(" Color: rgb({}, {}, {})", r, g, b),
}
}
fn classify_number(n: i32) -> &'static str {
match n {
0 => "zero",
1..=9 => "a single-digit number",
10..=99 => "a two-digit number",
100..=999 => "a three-digit number",
_ => "a big number",
}
}
fn main() {
// Match on enums with data
println!("--- Processing Commands ---");
let commands = vec![
Command::Echo(String::from("hello")),
Command::Move { x: 10, y: 20 },
Command::Color(255, 128, 0),
Command::Quit,
];
for cmd in &commands {
process_command(cmd);
}
// Match on numbers with ranges
println!("\n--- Number Classification ---");
for n in [0, 5, 42, 500, 10000] {
println!(" {} is {}", n, classify_number(n));
}
// Match with guards
println!("\n--- Match Guards ---");
let pair = (2, -3);
let description = match pair {
(x, y) if x > 0 && y > 0 => "both positive",
(x, y) if x < 0 && y < 0 => "both negative",
(_, 0) | (0, _) => "one is zero",
_ => "mixed signs",
};
println!(" ({}, {}) -> {}", pair.0, pair.1, description);
// Destructuring tuples
println!("\n--- Tuple Destructuring ---");
let point = (3, 7);
let (x, y) = point;
println!(" x = {}, y = {}", x, y);
// if let: concise single-pattern matching
println!("\n--- if let ---");
let favorite: Option<&str> = Some("Rust");
if let Some(lang) = favorite {
println!(" Favorite language: {}", lang);
}
let nothing: Option<i32> = None;
if let Some(val) = nothing {
println!(" Found: {}", val);
} else {
println!(" Nothing found -- and that's ok!");
}
}Challenge
Add a ChangeSpeed(i32) variant to the Command enum and handle it in process_command. Then add a ChangeSpeed command to the commands vector.
Previous: Enums
Next: Error Handling