Patterns are a powerful feature in Rust that allow you to destructure values, control program flow, and handle different data structures in a concise and expressive way. They are fundamental to Rust's type system and error handling. Here's a detailed look at the various contexts where patterns can be employed:
1. `let` Statements: Patterns are used on the left-hand side of `let` statements to bind values to names. This is commonly used for destructuring tuples, structs, and enums.
* Example: `let (x, y) = (1, 2);` or `let Point { x, y } = my_point;`
2. `match` Expressions: This is the most explicit and comprehensive use of patterns. `match` expressions allow you to compare a value against a series of patterns and execute code based on which pattern the value matches. `match` expressions must be *exhaustive*, meaning all possible values of the type must be covered by at least one pattern.
* Example: `match my_enum_value { SomeVariant(data) => ..., OtherVariant => ... }`
3. `if let` Expressions: `if let` is a convenient shorthand for a `match` expression that only cares about one specific pattern and ignores all other cases. It's often used for handling `Option<T>` or `Result<T, E>` values when you only care about the `Some` or `Ok` case.
* Example: `if let Some(value) = my_option { ... }`
4. `while let` Loops: Similar to `if let`, `while let` allows you to loop as long as a value matches a particular pattern. This is particularly useful for processing items from a collection or stream until a specific variant (like `None` or `Err`) is encountered.
* Example: `while let Some(item) = my_stack.pop() { ... }`
5. `for` Loops: Patterns can be used in `for` loops to destructure elements yielded by an iterator. This is very common when iterating over collections of tuples or structs.
* Example: `for (key, value) in my_map.iter() { ... }`
6. Function Parameters: You can use patterns directly in function signatures to destructure arguments as they are passed into the function. This can make function definitions cleaner and more readable.
* Example: `fn print_coords((x, y): (i32, i32)) { ... }`
7. Closure Parameters: Just like function parameters, patterns can be used in closure definitions to destructure arguments. This works identically to function parameters.
* Example: `let print_point = |Point { x, y }| { ... };`
In all these contexts, patterns provide a structured way to extract data from complex types, control flow based on the shape of data, and write more robust and readable Rust code.
Example Code
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
struct Point {
x: i32,
y: i32,
}
// 6. Patterns in Function Parameters
fn process_point((x, y): (i32, i32)) { // Destructuring a tuple argument
println!("Function: Processing point ({}, {})", x, y);
}
fn process_message_move(Message::Move { x, y }: Message) { // Destructuring an enum variant argument
// Note: This function will panic if called with a non-Move message variant
println!("Function: Direct move message processing: x={}, y={}", x, y);
}
fn main() {
// 1. Patterns in `let` Statements
let (a, b, c) = (1, 2, 3); // Destructuring a tuple
println!("Let: a: {}, b: {}, c: {}", a, b, c);
let p = Point { x: 10, y: 20 };
let Point { x: p_x, y: p_y } = p; // Destructuring a struct (can rename fields)
println!("Let: Struct point p_x: {}, p_y: {}", p_x, p_y);
// Or simply `let Point { x, y } = p;` if field names are desired.
// 2. Patterns in `match` Expressions
let msg = Message::ChangeColor(255, 128, 64);
match msg {
Message::Quit => println!("Match: The Quit variant."),
Message::Move { x: x_coord, y: y_coord } => { // Destructuring struct fields in enum
println!("Match: Move to x: {}, y: {}", x_coord, y_coord);
}
Message::Write(text) => println!("Match: Text message: {}", text),
Message::ChangeColor(r, g, b) => { // Destructuring tuple fields in enum
println!("Match: Change color to R: {}, G: {}, B: {}", r, g, b);
}
}
// 3. Patterns in `if let` Expressions
let some_value = Some(3);
if let Some(value) = some_value { // Matching `Some` variant of Option
println!("If Let: Got a value from Some: {}", value);
} else {
println!("If Let: Got Nothing.");
}
let msg2 = Message::Move { x: 5, y: 10 };
if let Message::Move { x: move_x, y: move_y } = msg2 { // Matching an enum variant
println!("If Let: Accessed move message with if let: x={}, y={}", move_x, move_y);
}
// 4. Patterns in `while let` Loops
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { // Looping as long as `pop()` returns `Some`
println!("While Let: Popped {} from stack.", top);
}
// 5. Patterns in `for` Loops
let pairs = vec![(1, 'a'), (2, 'b'), (3, 'c')];
for (num, char_val) in pairs { // Destructuring a tuple in a for loop
println!("For Loop: Number: {}, Character: {}", num, char_val);
}
let messages_list = vec![
Message::Write(String::from("Hello")),
Message::Move { x: 1, y: 2 },
Message::Quit,
];
for msg_item in messages_list {
if let Message::Write(text) = msg_item { // Nested if let within for loop is common
println!("For Loop + If Let: Found a write message: {}", text);
}
}
// 6. Patterns in Function Parameters (already defined above)
process_point((100, 200));
// process_message_move(Message::Quit); // This would panic!
process_message_move(Message::Move { x: 77, y: 88 });
// 7. Patterns in Closure Parameters
let print_coords = |Point { x, y }| { // Destructuring a struct argument in a closure
println!("Closure: Received x: {}, y: {}", x, y);
};
print_coords(Point { x: 30, y: 40 });
let sum_tuple_elements = |(val1, val2): (i32, i32)| {
println!("Closure: Sum of tuple elements: {}", val1 + val2);
};
sum_tuple_elements((5, 7));
}
```








All the Places Patterns Can Be Used in Rust