๐ฏ Pattern Matching in Rust: The Ultimate Sorting Machine
Imagine you have a magical sorting machine. You put something in, and it automatically figures out what it is and does the right thing!
๐ช The Story: The Magic Sorting Hat
Remember the sorting hat from Harry Potter? It looks at a student and decides which house they belong to. Rustโs pattern matching works exactly like that!
You give Rust a value, and it looks at the pattern to decide what to do.
๐ญ Match Expression: The Decision Machine
Think of match like a vending machine. You put in a coin, and based on what coin it is, you get different snacks!
let coin = "quarter";
match coin {
"penny" => println!("1 cent"),
"nickel" => println!("5 cents"),
"dime" => println!("10 cents"),
"quarter" => println!("25 cents"),
_ => println!("Unknown coin"),
}
What happens here?
- Rust checks: Is it a penny? No.
- Is it a nickel? No.
- Is it a dime? No.
- Is it a quarter? YES! โ Print โ25 centsโ
๐ Real-Life Example
let fruit = "apple";
let color = match fruit {
"apple" => "red",
"banana" => "yellow",
"grape" => "purple",
_ => "unknown",
};
println!("The {} is {}", fruit, color);
// Output: The apple is red
โ Exhaustive Matching: You Must Cover EVERYTHING!
Imagine a traffic light. It can be red, yellow, or green. What if you forgot to tell the car what to do on yellow?
Rust says: โNO WAY! You must handle ALL possibilities!โ
enum TrafficLight {
Red,
Yellow,
Green,
}
let light = TrafficLight::Red;
match light {
TrafficLight::Red => println!("Stop!"),
TrafficLight::Yellow => println!("Slow down!"),
TrafficLight::Green => println!("Go!"),
// If you remove any line above,
// Rust will complain!
}
๐ง Why is this awesome?
- You can never forget a case
- Your code is safer
- No surprises at runtime!
๐ฃ Catch-All Patterns: The Safety Net
Sometimes you canโt list every possibility. Thatโs where the catch-all comes in!
The _ Pattern (Underscore)
The underscore is like saying: โI donโt care what it is, just do this!โ
let number = 42;
match number {
1 => println!("One!"),
2 => println!("Two!"),
3 => println!("Three!"),
_ => println!("Some other number"),
}
// Output: Some other number
Named Catch-All
Sometimes you want to use the value you caught:
let number = 42;
match number {
1 => println!("One!"),
2 => println!("Two!"),
other => println!("Got: {}", other),
}
// Output: Got: 42
Rule: The catch-all MUST be last!
๐ The matches! Macro: Quick True/False Check
Sometimes you just want to ask: โDoes this match that pattern?โ
Think of it like asking: โIs this a dog?โ โ Yes or No!
let pet = "dog";
let is_dog = matches!(pet, "dog");
println!("Is it a dog? {}", is_dog);
// Output: Is it a dog? true
More Examples
let number = 5;
// Is it between 1 and 10?
let in_range = matches!(number, 1..=10);
println!("In range? {}", in_range);
// Output: In range? true
// Is it 1, 2, or 3?
let small = matches!(number, 1 | 2 | 3);
println!("Is small? {}", small);
// Output: Is small? false
When to use matches!?
- When you need a simple yes/no answer
- When you want clean, readable code
- Perfect for conditions in if statements
๐ if let: Unwrap ONE Pattern Only
Sometimes you only care about one specific case. Writing a full match feels like overkill!
The Problem
let gift = Some("toy");
// Full match - feels too long!
match gift {
Some(item) => println!("Got: {}", item),
None => {} // We don't care about None!
}
The Solution: if let
let gift = Some("toy");
if let Some(item) = gift {
println!("Got: {}", item);
}
// Output: Got: toy
Translation: โIf gift matches Some(item), then print it!โ
With else
let gift: Option<&str> = None;
if let Some(item) = gift {
println!("Got: {}", item);
} else {
println!("No gift today!");
}
// Output: No gift today!
๐ while let: Keep Going While Pattern Matches
Imagine eating cookies from a jar. You keep eating while there are cookies!
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
// Output:
// Popped: 3
// Popped: 2
// Popped: 1
What happens?
- Pop 3 โ matches Some(3) โ print it
- Pop 2 โ matches Some(2) โ print it
- Pop 1 โ matches Some(1) โ print it
- Pop nothing โ returns None โ loop stops!
๐ฎ Real Use Case: Processing a Queue
let mut tasks = vec!["email", "code", "lunch"];
while let Some(task) = tasks.pop() {
println!("Doing: {}", task);
}
println!("All done!");
๐ก๏ธ let else: Match or ESCAPE!
This is the newest pattern! It says: โMatch this pattern, OR ELSE get out of here!โ
fn get_name(input: Option<&str>) -> &str {
let Some(name) = input else {
return "Anonymous";
};
name
}
println!("{}", get_name(Some("Alice")));
// Output: Alice
println!("{}", get_name(None));
// Output: Anonymous
How it works
- Try to match the pattern
- If it matches: Keep going with the value
- If it doesnโt: Run the else block (must exit!)
The else block MUST diverge!
The else block must do something that never returns normally:
return- exit the functionbreak- exit a loopcontinue- skip to next iterationpanic!()- crash the program
fn process(value: Option<i32>) -> i32 {
let Some(x) = value else {
panic!("Value was None!");
};
x * 2
}
๐บ๏ธ The Pattern Matching Family
graph TD A["Pattern Matching"] --> B["match"] A --> C["if let"] A --> D["while let"] A --> E["let else"] A --> F["matches!"] B --> B1["Full control"] B --> B2["Must be exhaustive"] C --> C1["One pattern"] C --> C2["Optional else"] D --> D1["Loop while matches"] E --> E1["Match or exit"] F --> F1["Returns bool"]
๐ฏ Quick Comparison
| Tool | Use Whenโฆ | Returns |
|---|---|---|
match |
You need to handle ALL cases | Any type |
if let |
You care about ONE case | Nothing |
while let |
You want to loop until no match | Nothing |
let else |
Match or exit immediately | The matched value |
matches! |
You need true/false answer | bool |
๐ Putting It All Together
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
Color(i32, i32, i32),
}
fn handle(msg: Message) {
// Full match for complex handling
match msg {
Message::Quit => {
println!("Goodbye!");
}
Message::Move { x, y } => {
println!("Move to ({}, {})", x, y);
}
Message::Write(text) => {
println!("Message: {}", text);
}
Message::Color(r, g, b) => {
println!("Color: RGB({},{},{})", r, g, b);
}
}
}
๐ You Did It!
You now understand Rustโs powerful pattern matching:
- โ
match- The full decision machine - โ Exhaustive matching - Cover all cases!
- โ
Catch-all
_- The safety net - โ
matches!- Quick true/false - โ
if let- Handle one case - โ
while let- Loop until no match - โ
let else- Match or escape!
Pattern matching is like having superpowers for handling data. Youโll never look at conditionals the same way again! ๐ฆโจ
