What is Pattern Matching?
Pattern matching in Rust is a mechanism to compare a value against a series of patterns and execute code based on the first pattern that matches. It is primarily achieved using the match and if let constructs.
- match: A comprehensive tool for pattern matching.
- if let: A shorthand for simpler cases.
The Syntax of Pattern Matching
Basic Syntax of match:
match value {
pattern1 => action1,
pattern2 => action2,
_ => default_action,
}
- value: The value being matched.
- pattern: The pattern to match against.
- action: The code executed if the pattern matches.
- _: A catch-all pattern for unmatched cases.
Examples of Pattern Matching
1. Basic Match
fn main() {
let number = 2;
match number {
1 => println!("One"),
2 => println!("Two"),
3 => println!("Three"),
_ => println!("Something else"),
}
}
Output:
Two
2. Matching with Ranges
You can match numeric ranges using . .=.
fn main() {
let number = 15;
match number {
1..=10 => println!("Between 1 and 10"),
11..=20 => println!("Between 11 and 20"),
_ => println!("Outside the range"),
}
}
Output:
Between 11 and 20
3. Matching Enums
Enums are a natural fit for pattern matching.
enum TrafficLight {
Red,
Yellow,
Green,
}
fn main() {
let light = TrafficLight::Green;
match light {
TrafficLight::Red => println!("Stop!"),
TrafficLight::Yellow => println!("Get ready to move."),
TrafficLight::Green => println!("Go!"),
}
}
Output:
Go!
4. Matching with Tuples
Tuples can also be destructured using patterns.
fn main() {
let coordinates = (3, 7);
match coordinates {
(0, 0) => println!("Origin"),
(x, 0) => println!("Point on X-axis at {}", x),
(0, y) => println!("Point on Y-axis at {}", y),
(x, y) => println!("Point at ({}, {})", x, y),
}
}
Output:
Point at (3, 7)
5. Ignoring Values with _
The _ pattern matches any value and can be used to ignore values.
fn main() {
let data = (10, 20);
match data {
(x, _) => println!("First value is {}", x),
}
}
Output:
First value is 10
6. Destructuring Structs
Pattern matching can destructure structs to access their fields.
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 5, y: 10 };
match point {
Point { x, y: 0 } => println!("Point on X-axis at {}", x),
Point { x: 0, y } => println!("Point on Y-axis at {}", y),
Point { x, y } => println!("Point at ({}, {})", x, y),
}
}
Output:
Point at (5, 10)
7. Using Guards in Patterns
You can add conditions to patterns with guards.
fn main() {
let number = 15;
match number {
n if n % 2 == 0 => println!("Even number: {}", n),
n if n % 2 != 0 => println!("Odd number: {}", n),
_ => println!("No match"),
}
}
Output:
Odd number: 15
Using if let for Simpler Matches
When only one pattern needs to be matched, if let can simplify your code.
fn main() {
let number = Some(5);
if let Some(value) = number {
println!("The value is {}", value);
} else {
println!("No value");
}
}
Output:
The value is 5
Advanced Concepts in Pattern Matching
1. Combining Patterns with |
You can match multiple patterns with a single arm using |.
fn main() {
let number = 1;
match number {
1 | 2 | 3 => println!("Matched 1, 2, or 3"),
_ => println!("No match"),
}
}
Output:
Matched 1, 2, or 3
2. Nested Patterns
Patterns can be nested for complex data structures.
enum Vehicle {
Car { brand: String, year: u32 },
Bike { model: String },
}
fn main() {
let vehicle = Vehicle::Car {
brand: String::from("Toyota"),
year: 2021,
};
match vehicle {
Vehicle::Car { brand, year } => println!("Car: {}, Year: {}", brand, year),
Vehicle::Bike { model } => println!("Bike: {}", model),
}
}
Output:
Car: Toyota, Year: 2021