Skip to content

enum、match与模式匹配

1. 这是什么

在 Rust 里,enum 用来表示:一个值可能属于几种不同形态中的某一种
match 和模式匹配则是 Rust 用来安全地拆开这些形态、逐一处理的核心手段。

一句话理解:

  • struct 更像“这个东西始终长这样”
  • enum 更像“这个东西可能有几种状态”
  • match 则是在问:它现在到底是哪一种

这套机制在 Rust 里非常核心,因为很多标准库类型本身就是基于 enum 的,比如:

  • Option<T>
  • Result<T, E>
  • 各种消息、状态机、命令类型

2. 为什么重要

很多语言也有枚举,但 Rust 的 enum 能力更强。
它不只是“几个固定常量”,还可以让每个分支携带不同数据。

这意味着你可以非常自然地表达:

  • 一个操作的不同状态
  • 一条消息的不同类型
  • 一个函数的成功 / 失败结果
  • 一套业务流程中的不同分支

match 的价值在于:

  • 强制你把可能情况想清楚
  • 编译期检查分支是否处理完整
  • 让状态流转更明确

所以它不仅是语法工具,也是 Rust 代码可读性和安全性的重要来源。

3. 先建立直觉

先看一个最简单的 enum:

rust
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

这里表示一个方向值只能是四种情况之一。

然后用 match 处理:

rust
fn describe(dir: Direction) {
    match dir {
        Direction::Up => println!("向上"),
        Direction::Down => println!("向下"),
        Direction::Left => println!("向左"),
        Direction::Right => println!("向右"),
    }
}

这段代码的关键不只是“分支判断”,而是:

  • 编译器知道 Direction 只有这四种可能
  • 如果你漏掉一种,编译器通常不会放过你

这和很多语言里靠 if/else + 默认分支兜底的感觉很不一样。

4. 核心内容

4.1 enum 不只是常量集合

Rust 的 enum 分支可以携带数据:

rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

这说明:

  • Quit 不带额外信息
  • Move 带结构化坐标
  • Write 带一段字符串
  • ChangeColor 带三个颜色值

所以 enum 更像“多个不同结构的总和类型”。

4.2 match 的基本写法

例如:

rust
fn handle(msg: Message) {
    match msg {
        Message::Quit => println!("退出"),
        Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
        Message::Write(text) => println!("文本:{}", text),
        Message::ChangeColor(r, g, b) => println!("颜色:{}, {}, {}", r, g, b),
    }
}

这里 match 不只是判断类型,还会顺手把数据拆出来。

你可以把它理解为:

  • 一边确认“现在是哪一类”
  • 一边把这一类里带的值拿出来用

这就是模式匹配最有力量的地方。

4.3 模式匹配不是只给 enum 用

虽然 enum 是最典型场景,但模式匹配还能匹配很多东西,例如元组:

rust
fn print_point(p: (i32, i32)) {
    match p {
        (0, 0) => println!("原点"),
        (x, 0) => println!("在 x 轴上:{}", x),
        (0, y) => println!("在 y 轴上:{}", y),
        (x, y) => println!("普通点:({}, {})", x, y),
    }
}

所以“模式匹配”不是某个零碎语法,而是一种统一的解构思维。

4.4 _ 和兜底分支

如果你不想逐个写完所有情况,也可以用 _ 代表“其他情况”:

rust
match number {
    1 => println!("one"),
    2 => println!("two"),
    _ => println!("other"),
}

但要注意:

  • _ 虽然方便
  • 也会隐藏掉“其实你漏想了某些状态”的信号

所以在业务状态非常明确时,通常更推荐尽量写完整分支,而不是一上来就用 _ 糊过去。

4.5 if let:只关心一种情况时更简洁

有时你并不需要完整 match,只关心一种模式,这时可以用 if let

rust
let maybe_name = Some(String::from("Rust"));

if let Some(name) = maybe_name {
    println!("name = {}", name);
}

它适合这种语义:

  • 如果是我关心的那种情况,就处理
  • 其他情况我暂时不展开

所以:

  • 分支全面处理,用 match
  • 只抓某一种情况,用 if let

4.6 match 的穷尽性检查为什么重要

Rust 的 match 很强调 exhaustive(穷尽性)。
也就是说,一个值所有可能情况都必须被覆盖。

这件事在状态机、协议消息、错误处理里特别有价值,因为它能让你在新增状态时,编译器自动提醒:

  • 还有哪些地方没更新
  • 哪些逻辑分支没处理新情况

这比“运行后某个分支悄悄掉进 default”要可靠得多。

4.7 用 enum 表达状态,比魔法值更清晰

例如很多初学代码会写:

rust
let status = 2;

然后自己记:

  • 1 = pending
  • 2 = running
  • 3 = done

这种写法非常脆弱。
更好的方式是:

rust
enum JobStatus {
    Pending,
    Running,
    Done,
}

这样:

  • 语义更清晰
  • 分支更安全
  • 维护成本更低

这也是 Rust 鼓励的建模方式:先把状态表达清楚,再写逻辑。

5. 常见误区

5.1 误区一:enum 就是别的语言那种整数枚举

不完全是。
Rust 的 enum 更接近“带数据的联合类型”,表达力强得多。

如果只把它理解成几个常量名,会低估它的作用。

5.2 误区二:match 就是 switch 换个名字

也不对。
match 的能力比很多语言的 switch 更强,因为它:

  • 可以解构数据
  • 可以匹配复杂模式
  • 有穷尽性检查
  • 和 enum / tuple / Option / Result 等深度结合

5.3 误区三:有 _ 就可以一路偷懒

_ 很方便,但如果你在关键业务状态里过度使用它,等于主动放弃了一部分编译期保护。

当你希望状态机更可靠时,完整列出分支往往更值得。

5.4 误区四:状态很多就别用 enum

恰恰相反。
状态一多,越应该优先考虑 enum,因为它能把“可能取值范围”收紧,让逻辑边界更明确。

6. 一个更实用的判断思路

写 Rust 时如果发现自己在做这些事:

  • 用整数 / 字符串表示一堆状态
  • 写很多 if value == ...
  • 某个变量可能代表几种完全不同形态

就可以问自己:

  1. 这里是不是更适合建模成 enum
  2. 各个状态之间是否应该携带不同数据
  3. 这里是该用完整 match,还是 if let 就够了
  4. 我是不是用 _ 掩盖了本该显式处理的情况

这样通常能把代码从“堆逻辑”提升到“先建模再处理”。

7. 学习建议

学习 enum / match 时,建议按这个顺序:

  1. 先写不带数据的简单 enum
  2. 再写带数据的 enum
  3. 再用 match 去完整处理它
  4. 再练 if let 和解构写法
  5. 最后再回头看 OptionResult,你会更容易看懂

因为很多 Rust 标准库设计,本质上都建立在这套思维上。

8. 自测标准

  • 能解释 enum 和 struct 在建模上的区别
  • 能看懂带数据的 enum 分支是什么意思
  • 能写出一个完整的 match 分支处理
  • 能解释为什么 match 的穷尽性检查很重要
  • 能判断什么时候该用 match,什么时候 if let 更合适