Giao diện
Pattern Matching & Destructuring Advanced
Pattern Matching: Công cụ mạnh nhất của Rust
Match Expression Basics
Exhaustiveness Checking
Rust compiler đảm bảo tất cả cases được handle:
rust
enum Status {
Active,
Inactive,
Pending,
}
fn handle(status: Status) -> &'static str {
match status {
Status::Active => "Running",
Status::Inactive => "Stopped",
// ERROR nếu thiếu Status::Pending!
Status::Pending => "Waiting",
}
}
// Dùng _ để catch-all
fn handle_partial(status: Status) -> &'static str {
match status {
Status::Active => "Running",
_ => "Other", // Catch tất cả cases còn lại
}
}Match với Ranges
rust
fn grade(score: u32) -> char {
match score {
90..=100 => 'A', // Inclusive range
80..=89 => 'B',
70..=79 => 'C',
60..=69 => 'D',
0..=59 => 'F',
_ => panic!("Invalid score"),
}
}
fn classify_char(c: char) -> &'static str {
match c {
'a'..='z' => "lowercase",
'A'..='Z' => "uppercase",
'0'..='9' => "digit",
_ => "other",
}
}Destructuring
Struct Destructuring
rust
struct Point { x: i32, y: i32 }
struct Rectangle { top_left: Point, bottom_right: Point }
fn main() {
let rect = Rectangle {
top_left: Point { x: 0, y: 10 },
bottom_right: Point { x: 20, y: 0 },
};
// Destructure nested structs
let Rectangle {
top_left: Point { x: x1, y: y1 },
bottom_right: Point { x: x2, y: y2 },
} = rect;
println!("Width: {}, Height: {}", x2 - x1, y1 - y2);
// Partial destructure với ..
let Point { x, .. } = rect.top_left;
println!("x only: {}", x);
}Enum Destructuring
rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
fn process(msg: Message) {
match msg {
Message::Quit => println!("Goodbye"),
// Destructure named fields
Message::Move { x, y } => {
println!("Move to ({}, {})", x, y);
}
// Destructure tuple variant
Message::Write(text) => {
println!("Message: {}", text);
}
// Destructure với rename
Message::ChangeColor(r, g, b) => {
println!("RGB({}, {}, {})", r, g, b);
}
}
}Tuple & Array Destructuring
rust
fn main() {
// Tuple destructuring
let (a, b, c) = (1, 2, 3);
let (first, .., last) = (1, 2, 3, 4, 5); // first=1, last=5
// Array destructuring
let [x, y, z] = [1, 2, 3];
let [head, tail @ ..] = [1, 2, 3, 4, 5]; // head=1, tail=[2,3,4,5]
// Slice destructuring
let slice: &[i32] = &[1, 2, 3, 4, 5];
match slice {
[] => println!("empty"),
[single] => println!("one: {}", single),
[first, second] => println!("two: {}, {}", first, second),
[first, .., last] => println!("first: {}, last: {}", first, last),
}
}Pattern Guards
if Guards trong Match
rust
fn categorize(num: i32) -> &'static str {
match num {
n if n < 0 => "negative",
n if n == 0 => "zero",
n if n < 10 => "single digit positive",
n if n < 100 => "double digit",
_ => "large",
}
}
// Guards với destructuring
enum Event {
Click { x: i32, y: i32 },
KeyPress(char),
}
fn handle_event(event: Event) {
match event {
Event::Click { x, y } if x < 0 || y < 0 => {
println!("Click outside bounds");
}
Event::Click { x, y } if x == y => {
println!("Click on diagonal at {}", x);
}
Event::Click { x, y } => {
println!("Click at ({}, {})", x, y);
}
Event::KeyPress(c) if c.is_ascii_alphabetic() => {
println!("Letter: {}", c);
}
Event::KeyPress(c) => {
println!("Other key: {}", c);
}
}
}⚠️ GUARDS VÀ EXHAUSTIVENESS
Pattern guards không được tính trong exhaustiveness check. Compiler không thể prove guards cover all cases:
rust
fn risky(opt: Option<i32>) -> i32 {
match opt {
Some(n) if n > 0 => n,
Some(n) if n <= 0 => -n,
None => 0,
// Compiler vẫn yêu cầu wildcard vì guards không exhaustive
_ => unreachable!(), // Hoặc Some(_) => ...
}
}@ Bindings
Bind giá trị trong khi match pattern:
rust
fn describe(num: i32) {
match num {
// Bind giá trị vào n trong khi check range
n @ 1..=12 => println!("Month {}", n),
n @ 13..=31 => println!("Day {}", n),
n @ _ => println!("Other: {}", n),
}
}
// @ với nested patterns
enum Message {
Hello { id: i32 },
}
fn process(msg: Message) {
match msg {
Message::Hello { id: id_var @ 3..=7 } => {
println!("ID in range [3,7]: {}", id_var);
}
Message::Hello { id } => {
println!("Other ID: {}", id);
}
}
}
// @ với references
fn process_slice(data: &[i32]) {
match data {
// Bind entire slice while destructuring
all @ [first, .., last] => {
println!("Slice of {} items, first={}, last={}",
all.len(), first, last);
}
[single] => println!("Single: {}", single),
[] => println!("Empty"),
}
}Or Patterns
Sử dụng | để match nhiều patterns:
rust
fn is_vowel(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u' | 'A' | 'E' | 'I' | 'O' | 'U')
}
fn categorize(num: i32) -> &'static str {
match num {
0 | 1 => "binary digit",
2 | 3 | 5 | 7 => "small prime",
n if n < 0 => "negative",
_ => "other positive",
}
}
// Or patterns với destructuring
enum Command {
Start,
Stop,
Restart,
Pause,
Resume,
}
fn is_lifecycle_command(cmd: &Command) -> bool {
matches!(cmd, Command::Start | Command::Stop | Command::Restart)
}if let và while let
if let - Simplified Match
rust
fn main() {
let config_max = Some(3u8);
// Thay vì match đầy đủ:
// match config_max {
// Some(max) => println!("Max: {}", max),
// _ => (),
// }
// Dùng if let:
if let Some(max) = config_max {
println!("Max: {}", max);
}
// if let với else
if let Some(max) = config_max {
println!("Max: {}", max);
} else {
println!("No max configured");
}
}while let - Loop với Pattern
rust
fn main() {
let mut stack = vec![1, 2, 3];
// Pop until empty
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
// Iterator pattern
let mut iter = (0..5).into_iter();
while let Some(n) = iter.next() {
println!("Got: {}", n);
}
}let else (Rust 1.65+)
rust
fn process(data: Option<String>) {
// Early return nếu pattern fail
let Some(value) = data else {
println!("No data");
return;
};
// value available here
println!("Processing: {}", value);
}
fn parse_config(input: &str) -> Result<Config, Error> {
let Some(first_line) = input.lines().next() else {
return Err(Error::EmptyInput);
};
// Continue with first_line...
Ok(Config { name: first_line.to_string() })
}matches! Macro
Boolean check với pattern:
rust
fn main() {
let num = Some(4);
// Thay vì:
// match num { Some(x) if x > 3 => true, _ => false }
// Dùng matches!:
let is_big = matches!(num, Some(x) if x > 3);
println!("Is big: {}", is_big); // true
// Hữu ích trong filter
let letters = ['a', '1', 'b', '2', 'c'];
let only_letters: Vec<_> = letters
.iter()
.filter(|c| matches!(c, 'a'..='z' | 'A'..='Z'))
.collect();
// ['a', 'b', 'c']
}Refutable vs Irrefutable Patterns
Irrefutable Patterns
Luôn match — dùng trong let, function params:
rust
// Irrefutable - luôn match
let x = 5;
let (a, b) = (1, 2);
let Point { x, y } = point;
fn foo((x, y): (i32, i32)) { ... }Refutable Patterns
Có thể fail — dùng trong match, if let:
rust
// Refutable - có thể fail
if let Some(x) = option { ... }
while let Some(x) = iter.next() { ... }
// ❌ ERROR: refutable pattern trong let
// let Some(x) = option; // ERROR
// ✅ ĐÚNG: dùng let else
let Some(x) = option else { return; };Best Practices
Prefer Pattern Matching over if
rust
// ❌ C-style
if status == Status::Active {
// ...
} else if status == Status::Pending {
// ...
}
// ✅ Rust idiomatic
match status {
Status::Active => { /* ... */ }
Status::Pending => { /* ... */ }
_ => { /* ... */ }
}Avoid Overly Complex Patterns
rust
// ❌ Quá phức tạp
match data {
Some(Ok(Value { inner: Some(x @ 1..=10), .. })) if x % 2 == 0 => { ... }
_ => {}
}
// ✅ Rõ ràng hơn - tách thành steps
let value = match data {
Some(Ok(v)) => v,
_ => return,
};
if let Some(x @ 1..=10) = value.inner {
if x % 2 == 0 {
// ...
}
}Bảng Tóm tắt
rust
// === BASIC MATCH ===
match value {
Pattern => expression,
Pattern | Pattern => expression, // Or pattern
Pattern if guard => expression, // Guard
_ => expression, // Wildcard
}
// === DESTRUCTURING ===
let Point { x, y } = point; // Struct
let (a, b, c) = tuple; // Tuple
let [first, .., last] = array; // Array/Slice
// === BINDINGS ===
n @ 1..=10 => ... // @ binding
ref x => ... // Reference binding
ref mut x => ... // Mutable ref binding
// === SHORTCUTS ===
if let Pattern = value { ... } // Single pattern match
while let Pattern = expr { ... } // Loop with pattern
let Pattern = value else { ... }; // Early return
matches!(value, Pattern) // Boolean check