讲解

枚举(enum)表达「几种确定的可能性之一」。Rust 枚举比多数语言强得多:每个变体可以直接携带数据,而且各变体携带的数据类型和数量可以不同——enum Message { Quit, Move { x: i32, y: i32 }, Write(String) } 一个类型覆盖了「无数据、结构体式数据、单个值」三种形态。这让枚举成为建模业务状态(订单状态、消息类型、解析结果)的完美工具。

match 是枚举的官配:按变体分支处理,编译器强制穷尽——漏掉任何一个变体都会编译报错。这条「穷尽性检查」是重构时的安全网:给枚举加新变体,编译器会领着你把每一处 match 都补上。各分支是表达式,可以有返回值;分支里可以绑定变体内部的数据直接用;实在有兜不完的底,用 _ 通配(但要克制,见常见坑)。

当只关心一种变体、其余都不处理时,match 写一个分支加一个 _ 太啰嗦,if let 是精简版:if let Message::Write(text) = msg { ... },只匹配目标模式,不匹配就跳过。标准库里最重要的枚举 Option 和 Result 下一章专门讲,它们和 match / if let 的组合是 Rust 错误处理的基石。

示例

携带数据的枚举 + 穷尽 match:

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

fn describe(msg: &Message) -> String {
    match msg {
        Message::Quit => String::from("退出"),
        Message::Move { x, y } => format!("移动到 ({x}, {y})"),
        Message::Write(text) => format!("写下:{text}"),
    }
    // 少写任何一个分支都会编译失败——穷尽性是强制的
}

fn main() {
    let msgs = [
        Message::Write(String::from("你好")),
        Message::Move { x: 3, y: 4 },
        Message::Quit,
    ];
    for m in &msgs {
        println!("{}", describe(m));
    }
    assert_eq!(describe(&Message::Quit), "退出");
    assert_eq!(describe(&Message::Move { x: 1, y: 2 }), "移动到 (1, 2)");
}

if let 处理单一模式,match 返回值:

fn main() {
    let some_number = Some(7);

    // 只关心 Some 的情况
    if let Some(n) = some_number {
        println!("拿到了 {n}");
        assert_eq!(n, 7);
    }

    // match 是表达式,分支值就是结果
    let label = match some_number {
        Some(n) if n > 5 => "大数", // if 是额外的条件守卫
        Some(_) => "小数",
        None => "没有数",
    };
    assert_eq!(label, "大数");
    println!("分类:{label}");
}

常见坑

  • match 没穷尽:编译器报 non-exhaustive patterns,把漏掉的变体列得清清楚楚——照单补上,别用 _ 糊弄。
  • 滥用 _ 通配:新增变体时 _ 会默默吞掉它,穷尽检查的提醒就没了;业务分支尽量写全变体,_ 留给真正「无所谓」的场景。
  • if let 取代了该写的 match:只关心一个分支用 if let,关心多个分支用 match;把 if let 串成 else if let 长链时该换回 match。
  • 变体数据忘了解构:match 分支里直接写模式绑定(Message::Write(text)),别拿到整个枚举再手动拆。

小结

枚举变体可携带任意数据;match 强制穷尽、是表达式、能绑定内部数据;if let 精简单分支场景。下一章见两个最重要的枚举:Option 与 Result。