Skip to content

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 letwhile 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