🎯 Rust Pattern Matching: Advanced Patterns
The Story of the Super Detective 🕵️
Imagine you’re a super detective with a magical magnifying glass. This glass doesn’t just help you see things—it helps you find exactly what you’re looking for and grab just the pieces you need.
That’s what advanced pattern matching in Rust does! It’s like having superpowers to look at data and say:
- “I only want numbers between 1 and 10” → Range patterns
- “I want this, BUT only if something special is true” → Match guards
- “Catch it AND give it a name!” → Binding with @
- “Look at it without taking it!” → ref keyword
- And so much more!
Let’s become pattern matching superheroes! 🦸
🌈 Range Patterns: “I Want Numbers Between Here and There!”
Think of a number line. Sometimes you don’t want ONE specific number—you want ALL numbers in a range!
What is a Range Pattern?
It’s like saying: “I’ll accept any number from 1 to 5.”
let age = 7;
match age {
1..=5 => println!("Toddler!"),
6..=12 => println!("Kid!"),
13..=19 => println!("Teenager!"),
_ => println!("Adult!"),
}
// Output: Kid!
🔑 Key Points
| Syntax | Meaning |
|---|---|
1..=5 |
1 through 5 (includes 5) |
1..5 |
1 through 4 (excludes 5) |
// Works with characters too!
let letter = 'c';
match letter {
'a'..='m' => println!("First half!"),
'n'..='z' => println!("Second half!"),
_ => println!("Something else"),
}
// Output: First half!
Remember: The ..= means “include the last one too!”
🛡️ Match Guards: “Yes, BUT Only If…”
A match guard is like adding an extra condition. It’s the detective saying: “I found a match, but let me double-check something first!”
What is a Match Guard?
It’s an if statement AFTER the pattern.
let number = 4;
match number {
n if n % 2 == 0 => {
println!("{} is even!", n)
}
n => println!("{} is odd!", n),
}
// Output: 4 is even!
Real-World Example
let temperature = 25;
match temperature {
t if t < 0 => println!("Freezing! 🥶"),
t if t < 15 => println!("Cold! 🧥"),
t if t < 25 => println!("Nice! 😊"),
t if t < 35 => println!("Hot! 🥵"),
_ => println!("Too hot! 🔥"),
}
// Output: Hot! 🥵
The pattern matches first, THEN the guard checks!
🏷️ Binding with @ (At Symbol): “Catch AND Name It!”
Sometimes you want to match a pattern AND save the value with a name. The @ symbol is like putting a label on something you found!
What Does @ Do?
let age = 15;
match age {
young @ 1..=12 => {
println!("Young one: {} years", young)
}
teen @ 13..=19 => {
println!("Teenager: {} years", teen)
}
adult => println!("Adult: {} years", adult),
}
// Output: Teenager: 15 years
The Magic Explained
teen @ 13..=19
│ │
│ └── Pattern to match (13 to 19)
│
└── Name we give to the matched value
Another Example
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: special @ 3..=7,
} => {
println!("Special ID: {}", special)
}
Message::Hello { id } => {
println!("Other ID: {}", id)
}
}
// Output: Special ID: 5
👁️ The ref Keyword: “Look, Don’t Take!”
When matching, Rust normally takes ownership of what it matches. But sometimes you just want to look at something without taking it away!
The Problem
let name = String::from("Alice");
match name {
n => println!("Hello, {}", n),
}
// After this, 'name' is MOVED into 'n'
// We can't use 'name' anymore!
The Solution: ref
let name = String::from("Alice");
match name {
ref n => println!("Hello, {}", n),
}
// 'name' is still usable here!
println!("Name is: {}", name);
ref mut for Changing Values
let mut score = 10;
match score {
ref mut s => *s += 5,
}
println!("Score: {}", score);
// Output: Score: 15
Think of ref as taking a photo instead of taking the actual thing! 📸
✅ Irrefutable Patterns: “This ALWAYS Works!”
An irrefutable pattern is one that always matches. It’s like saying “I accept anything!”
Examples of Irrefutable Patterns
// This ALWAYS works
let x = 5; // x matches anything
// This ALWAYS works
let (a, b) = (1, 2); // Tuple destructure
// Function parameters are irrefutable
fn greet(name: String) {
println!("Hello, {}", name);
}
Where Must You Use Them?
| Place | Requires |
|---|---|
let statements |
Irrefutable |
| Function parameters | Irrefutable |
for loops |
Irrefutable |
// This WON'T compile! ❌
// let Some(x) = some_option;
// This works! ✅
if let Some(x) = some_option {
println!("{}", x);
}
❓ Refutable Patterns: “This MIGHT Not Match!”
A refutable pattern can fail to match. It’s like asking “Are you a dog?” to an unknown animal—it might be a cat!
Examples of Refutable Patterns
let maybe_number: Option<i32> = Some(42);
// Refutable: might be None!
match maybe_number {
Some(n) => println!("Got: {}", n),
None => println!("Nothing!"),
}
Where to Use Refutable Patterns
| Place | Refutable OK? |
|---|---|
match arms |
✅ Yes |
if let |
✅ Yes |
while let |
✅ Yes |
let statement |
❌ No |
// Perfect for if let
if let Some(value) = maybe_number {
println!("Value: {}", value);
}
// Perfect for while let
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{}", top);
}
📋 Slice Patterns: “Look Inside Arrays!”
Slice patterns let you match parts of arrays or slices. It’s like looking at a train and saying “I want the first car, the last car, and everything in between!”
Basic Slice Matching
let numbers = [1, 2, 3, 4, 5];
match numbers {
[first, .., last] => {
println!("First: {}, Last: {}", first, last)
}
}
// Output: First: 1, Last: 5
The .. (Rest Pattern)
The .. means “I don’t care about the middle stuff!”
let colors = ["red", "green", "blue", "yellow"];
match colors {
[first, second, ..] => {
println!("1st: {}, 2nd: {}", first, second)
}
}
// Output: 1st: red, 2nd: green
Matching Specific Lengths
let data = [1, 2, 3];
match data {
[] => println!("Empty!"),
[x] => println!("One: {}", x),
[x, y] => println!("Two: {}, {}", x, y),
[x, y, z] => println!("Three: {}, {}, {}", x, y, z),
_ => println!("Many!"),
}
// Output: Three: 1, 2, 3
Capturing the Rest
let numbers = [1, 2, 3, 4, 5];
match numbers {
[first, rest @ ..] => {
println!("First: {}", first);
println!("Rest: {:?}", rest);
}
}
// Output:
// First: 1
// Rest: [2, 3, 4, 5]
🔀 Or Patterns: “This OR That!”
Or patterns use | to match multiple possibilities. It’s like saying “I’ll take chocolate OR vanilla!”
Basic Or Pattern
let number = 2;
match number {
1 | 2 | 3 => println!("One, two, or three!"),
4 | 5 | 6 => println!("Four, five, or six!"),
_ => println!("Something else!"),
}
// Output: One, two, or three!
Combining with Other Patterns
let point = (0, 5);
match point {
(0, y) | (y, 0) => {
println!("On an axis: {}", y)
}
(x, y) => println!("Point: ({}, {})", x, y),
}
// Output: On an axis: 5
With Enums
enum Color {
Red,
Green,
Blue,
Yellow,
}
let color = Color::Blue;
match color {
Color::Red | Color::Yellow => {
println!("Warm color!")
}
Color::Blue | Color::Green => {
println!("Cool color!")
}
}
// Output: Cool color!
🎮 Putting It All Together!
Let’s combine everything we learned:
fn analyze(data: &[i32]) {
match data {
// Empty slice
[] => println!("No data!"),
// Single element in range
[x @ 1..=10] => {
println!("Single small: {}", x)
}
// Two elements, first is special
[first @ 1..=5, second]
if *second > 10 => {
println!(
"Special pair: {} and {}",
first, second
)
}
// First, last, and rest
[first, rest @ .., last]
if first == last => {
println!("Bookends: {}", first);
println!("Middle: {:?}", rest);
}
// Catch all
_ => println!("Other pattern"),
}
}
fn main() {
analyze(&[5, 20]);
// Output: Special pair: 5 and 20
analyze(&[3, 4, 5, 3]);
// Output: Bookends: 3
// Middle: [4, 5]
}
🌟 Quick Reference
graph TD A["Advanced Patterns"] --> B["Range 1..=10"] A --> C["Guard if x > 0"] A --> D["Binding n @ pattern"] A --> E["ref keyword"] A --> F["Irrefutable always match"] A --> G["Refutable might fail"] A --> H["Slice first, .., last"] A --> I["Or A | B | C"]
🎯 Remember These Superpowers!
| Pattern | Superpower | Example |
|---|---|---|
| Range | Match a range | 1..=5 |
| Guard | Extra condition | n if n > 0 |
| @ Binding | Name + Match | x @ 1..=10 |
| ref | Borrow in match | ref name |
| Irrefutable | Always matches | let x = 5 |
| Refutable | Might fail | Some(x) |
| Slice | Array parts | [first, .., last] |
| Or | Multiple options | 1 | 2 | 3 |
You now have the superpowers of a Rust pattern matching hero! 🦸♀️🦸♂️
Go forth and match with confidence! 🚀
