讲解
枚举(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。