The match Expression
The match expression is Rust's Swiss Army knife for control flow. You give it a value, and it runs the arm that matches. The catch? It must be exhaustive โ every possible value must be covered. This ensures you never forget to handle a case.
fn main() {
let number = 3;
match number {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("something else"),
}
}
Try it Yourself ->
Matching Enums
Pattern matching shines brightest when used with enums. You can match on enum variants and extract their inner data at the same time. It's clean, expressive, and the compiler guarantees you cover everything.
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
fn main() {
let addr = IpAddr::V4(192, 168, 1, 1);
match addr {
IpAddr::V4(a, b, c, d) => println!("{}.{}.{}.{}", a, b, c, d),
IpAddr::V6(addr) => println!("IPv6: {}", addr),
}
}
Try it Yourself ->
if let and while let
When you only care about one pattern, if let is more concise than a full match. It runs the block only if the value matches the pattern. Similarly, while let keeps looping as long as the pattern matches.
fn main() {
let some_value: Option<i32> = Some(42);
// if let: only care about Some
if let Some(value) = some_value {
println!("Got: {}", value);
}
// while let: keep popping until None
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
}
Try it Yourself ->
Destructuring Structs
Patterns can destructure structs to pull out their fields. This makes it easy to extract values from complex types without a bunch of field access calls.
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
match p {
Point { x: 0, y } => println!("On Y axis at {}", y),
Point { x, y: 0 } => println!("On X axis at {}", x),
Point { x, y } => println!("At ({}, {})", x, y),
}
}
Try it Yourself ->