Rust 枚举、模式匹配与 Option/Result
更新时间:2026-09-18。Rust 没有 null,也没有异常,这两个「缺失」都由枚举 + 模式匹配优雅替代。所有权见 所有权与借用规则。
一、枚举可以携带数据
Rust enum 每个变体可以带不同类型和数量的数据(代数数据类型 / sum type):
rust
#[derive(Debug)]
enum Message {
Quit, // 无数据
Move(i32, i32), // 元组变体
Write(String),
ChangeColor { r: u8, g: u8, b: u8 }, // 结构体变体
}二、match:穷尽匹配
rust
fn handle(m: Message) {
match m {
Message::Quit => println!("quit"),
Message::Move(x, y) => println!("move {x} {y}"),
Message::Write(s) => println!("{s}"),
Message::ChangeColor { r, g, .. } => println!("color {r} {g}"),
}
}match 必须覆盖所有可能,漏分支编译失败(新增枚举变体时编译器会在所有 match 处提醒你补全)。可用 _ 兜底。
三、Option<T>:类型层面没有 null
rust
enum Option<T> { Some(T), None }
fn find_user(id: u32) -> Option<String> {
if id == 1 { Some("alice".into()) } else { None }
}
let u = find_user(1);
let name = u.unwrap_or("匿名"); // 缺省值
if let Some(n) = find_user(2) { println!("{n}"); }
let len = find_user(1).map(|s| s.len()); // Some(5) / None,链式处理「可能没有值」被编码进类型,使用方必须显式处理 None,从根上消除空指针异常(对照其他语言的 NPE)。
四、Result<T, E>:错误是值,不是异常
rust
enum Result<T, E> { Ok(T), Err(E) }
fn parse_port(s: &str) -> Result<u16, std::num::ParseIntError> {
let p: u16 = s.parse()?; // ? : 成功得值,失败立即 return Err
Ok(p)
}
match parse_port("8080") {
Ok(p) => println!("port {p}"),
Err(e) => eprintln!("bad port: {e}"),
}?传播错误(还会做错误类型的From转换),让主流程不被样板打断。unwrap/expect在 None 或 Err 时 panic,只适合原型或确定不可能处;库/服务代码应显式处理或传播。- 惯用组合子:
map/map_err/and_then/unwrap_or_else。
五、常见坑
- match 忘记穷尽:加了新枚举变体后所有 match 都要补,编译器报错是帮你而非刁难。
- 匹配时移动了值:对
Option<String>直接Some(s)会 move;只需引用写match &opt { Some(s) => .. }或用as_ref()。 - 到处 unwrap:生产代码对外部输入
unwrap会因脏数据 panic,改用?/unwrap_or/匹配。 if let忽略其它分支:方便但会静默放过 None/Err,需要兜底时回到 match。?要求函数返回 Result/Option:在 main 里用要让 main 返回Result<(), E>或先处理。
总结
Rust enum 的每个变体可携带不同数据,配合必须穷尽的 match 做安全解构;标准库用 Option<T>(Some/None)把“可能没有值”编码进类型,强迫调用方处理 None,从而在类型层面消灭空指针,用 Result<T,E>(Ok/Err)把错误当成普通返回值显式处理、用 ? 自动传播错误而不靠异常;实战中用 unwrap_or/map/and_then 等组合子、用 if let 简化单分支匹配、对引用场景用 &opt 或 as_ref 避免移动,并避免在生产路径滥用会 panic 的 unwrap/expect。