跳至内容

模式匹配

这篇讲 Rust 最强大的控制流工具——模式匹配。match 表达式配上 Rust 的枚举,能写出比 Go 的 switch、Python 的 if-elif 更安全、更表达力的代码。编译器甚至会检查你是否覆盖了所有情况。

match 表达式

match 逐一将值与每个模式比较,执行第一个匹配到的分支:

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

match穷尽的(exhaustive)——编译器强制你必须覆盖所有可能的取值。如果忘了一个分支,代码无法编译:

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        // ❌ 缺少 Quarter 分支——编译错误!
    }
}

这是 Rust 给程序员的免费安全网——每次新增枚举变体,编译器会帮你找到所有需要更新的 match 表达式。

匹配带值的变体

模式可以「解构」变体中携带的数据:

#[derive(Debug)]
enum UsState {
    Alabama, Alaska, // ...
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),  // 携带一个州
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        }
    }
}

匹配 Option<T>

处理 Option<T>match 最常见的用途——这是在 Rust 中安全解包值的标准方式:

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);    // Some(6)
    let none = plus_one(None);   // None
}

通配模式和 _ 占位符

fn main() {
    let dice_roll = 9;

    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        other => move_player(other),  // other 绑定值
    }

    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => reroll(),               // _ 不绑定值,表示「其余全部」
    }
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(_spaces: u8) {}
fn reroll() {}
other 绑定匹配到的值(可以在代码块中使用),_ 不绑定值(告诉编译器「我不关心这个值」)。当不需要用到匹配值时,用 _ 更清晰。

if let

当只关心一种匹配情况而忽略其余时,if letmatch 更简洁:

fn main() {
    let config_max = Some(3u8);

    // match 写法(啰嗦)
    match config_max {
        Some(max) => println!("The maximum is configured to be {max}"),
        _ => (),
    }

    // if let 写法(简洁)
    if let Some(max) = config_max {
        println!("The maximum is configured to be {max}");
    }

    // 带 else 的 if let
    if let Some(max) = config_max {
        println!("max = {max}");
    } else {
        println!("没有配置最大值");
    }
}

匹配进阶技巧

匹配多个模式

let x = 1;
match x {
    1 | 2 => println!("one or two"),  // | 分隔多个模式
    3 => println!("three"),
    _ => println!("anything"),
}

匹配范围

let x = 5;
match x {
    1..=5 => println!("one through five"),   // 1 到 5(含 5)
    _ => println!("something else"),
}

守卫条件(match guard)

let num = Some(4);

match num {
    Some(x) if x < 5 => println!("小于 5: {x}"),  // 守卫条件
    Some(x) => println!("{x}"),
    None => (),
}

@ 绑定

let msg = Message::Hello { id: 5 };

match msg {
    Message::Hello { id: id_var @ 3..=7 } => {
        println!("id 在 3..=7 范围内: {id_var}");
    }
    Message::Hello { id: 10..=12 } => {
        println!("id 在 10..=12 范围内")
    }
    Message::Hello { id } => {
        println!("id 是其它值: {id}")
    }
}

enum Message {
    Hello { id: i32 },
}

一句话小结

match 是穷尽式的模式匹配——编译器强制覆盖所有情况,配合 Option<T>Result<T, E> 使用安全高效。if let 处理「只关心一种」的场景,守卫条件和 @ 绑定处理复杂条件。Go 程序员:这就是你在 switch 里梦想的功能。下一篇讲 Trait 与泛型

练习

  1. match 实现函数 describe_number(n: i32) -> &str

    • 负数 → “负数”
    • 0 → “零”
    • 正数(<=100)→ “小正数”
    • 正数(>100)→ “大正数”
  2. 有一个 Option<String> 变量 name,用 if let 打印「Hello, {name}!」,如果是 None 则打印「Hello, guest!」。

参考答案
fn describe_number(n: i32) -> &'static str {
    match n {
        ..=-1 => "负数",
        0 => "零",
        1..=100 => "小正数",
        _ => "大正数",
    }
}

fn main() {
    let name = Some(String::from("Rust"));

    // 方法 1:if let + else
    if let Some(n) = &name {
        println!("Hello, {n}!");
    } else {
        println!("Hello, guest!");
    }

    // 方法 2:match
    match &name {
        Some(n) => println!("Hello, {n}!"),
        None => println!("Hello, guest!"),
    }
}
最后更新于