本页目录

enum 与模式匹配

Rust 的 enum 是带标签的联合体(tagged union)——每个 variant 携带独立的数据,编译器保证 match 覆盖所有 variant(穷尽性检查)。Option 和 Result 不是语言内置的,而是标准库用 enum 定义的——这证明了 enum 是 Rust 表达能力最强的类型构造器。

enum 是 tagged union,不只是"枚举"

C 的 enum 只是一组命名整数常量。Rust 的 enum 每个 variant 可以携带不同的数据——它是一个带标签的联合体:

enum Message {
    Quit,                            // 无数据 — 纯标签
    Move { x: i32, y: i32 },         // 匿名 struct
    Write(String),                   // tuple variant
    ChangeColor(u8, u8, u8),         // 3 个 u8 的 tuple
}

编译器为 Message 分配的大小足以容纳最大的 variant(Move = 两个 i32 = 8 bytes),另加一个 discriminant(tag)字段。对于 Option<&T> 这类有 niche 的情况,tag 可以被优化掉。

Option: Rust 没有 null

Tony Hoare 称 null 是"十亿美元的错误"。Rust 没有 null——可能不存在的值用 Option<T> 表示:

fn safe_div(a: i32, b: i32) -> Option<i32> {
    if b == 0 { None } else { Some(a / b) }
}
// 调用者必须处理 None:
match safe_div(10, 0) {
    Some(result) => println!("{}", result),
    None => println!("division by zero"),
}

编译器强制你处理 None 的情况——不可能忘记检查。Option<T> 是普通类型,不是特殊的 null 标记,所以你可以对 Option 使用 map、and_then 等 combinators。

Result: 错误也是值

异常的问题是它打破了控制流——你无法从函数签名知道它可能抛出什么。Result<T, E> 把错误变成返回值的一部分:

fn parse(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.parse()                        // 返回 Result, 不是 throw
}

调用者看到 Result<i32, ParseIntError> 就知道——这个函数可能成功返回 i32,也可能失败返回解析错误。没有隐藏的控制流。

Match: 编译期穷尽检查

match number {
    1 => "one",
    2 => "two",
    // _ => "other",                 // 如果缺少这一行, 编译报错:
                                     // "non-exhaustive patterns"
}

编译器会检查 match 是否覆盖了所有可能的 variant。如果类型添加了新 variant(如 enum 加了一个成员),所有 match 这个类型的地方都会编译失败——强迫你显式处理新情况。

模式的各种形式

// 解构
let (x, y, z) = (1, 2, 3);
let Point { x, y } = point;

// ref 模式: 不 move, 借用
let ref r = value;                   // r: &T, value 未被 move

// match guard: 额外条件
match num {
    n if n % 2 == 0 => "even",
    n if n > 10 => "big",
    _ => "other",
}

// @ 绑定: 同时匹配 pattern 和绑定变量
match opt {
    Some(v @ 1..=5) => println!("small: {}", v),
    Some(v) => println!("large: {}", v),
    None => (),
}

// if let / while let: 只关心一个 variant 时的语法糖
if let Some(v) = opt { process(v); }
while let Some(v) = iter.next() { process(v); }

参考

  • Rust Book: Chapter 6 (Enums), Chapter 18 (Patterns)
  • Rust Reference: Match expressions, Patterns

Keywords: enum, Option, Result, match, pattern, if let, while let, exhaustiveness, tagged union