跳转到内容

第 6 章 枚举、模式匹配与错误建模

在第 5 章中,我们用结构体(struct)把多个同时存在的字段打包成一个所有权单元(积类型 Product Type)。但在真实工程中,数据建模还有另一半核心需求:表达「一个类型存在几种互斥的合法可能性(或关系)」。

许多从 C/C++ 或 Java 转过来的开发者,起初会把 Rust 的 enum(枚举)当成普通的「整数常量表」。而本章将带你认识 enum 的真实身份——代数数据类型(ADT)中的和类型(Sum Type),并看清 Rust 如何仅凭这一项机制,正面解决软件工程中最顽固的两个难题:空指针(Option<T>)与错误处理(Result<T, E>)。

本章按五步推进:

  1. 从「或关系」到固定长度内存(Tag + 共享载荷区):对比用结构体加标志位硬凑、OOP 用「父类 + 子类」模拟和类型的局限,拆解 Rust enum 如何在编译期闭合全部合法可能性,并在单块固定长度的栈内存中通过判别式标签与共享载荷区存放不同分支的数据。
  2. 模式匹配与编译期穷尽性检查:澄清「模式匹配等于 match」的常见误解,从 let 模式 = 表达式; 出发打通积类型与和类型的解构规则,掌握 match、if let 与 let else 的分工及模式绑定中的所有权流向。
  3. 消灭空指针(Option<T> 与空位优化 Niche):看清为什么 null 是隐式未闭合的和类型漏洞,理解 Option<T> 如何将「可能缺失」隔离为独立的编译期类型实体,并验证为什么 Option<&T> 在运行时依然只占 8 字节。
  4. 错误即数据(Result<T, E> 与 ? 短路传播):告别隐式中断控制流的异常跳转,将成功结果与失败原因并列写进函数返回签名,拆解 ? 运算符在编译期展开为 match 提前返回的机制。
  5. 工程表达力与 panic! 边界:用困难三角化解初学者的「拆包烦躁感」,掌握 unwrap_or、as_ref 与 take 三大工程转换手法,划清 Result 与 panic!(及 .unwrap() / .expect())的代码审查红线。

读完本章,检验自己的四个问题

Section titled “读完本章,检验自己的四个问题”
  • 为什么说 OOP 的「父类 + 子类」是在尝试实现和类型,但不如 Rust 的 enum 严密?在内存中,一个带数据的 enum 字节大小是由什么决定的?
  • 为什么元组和结构体可以直接写 let (a, b) = pair; 解构,而对枚举写单行 let Role::Member(p) = role; 会编译失败?match、if let 和 let else 分别是怎么解决这个失败分支的?
  • 为什么 size_of::<Option<u32>>() 比 u32 大,而 size_of::<Option<&i32>>() 和 size_of::<Option<String>>() 却与原类型完全相等?
  • ? 运算符在编译期展开成了什么代码?在代码审查中,面对 Option 或 Result,什么时候必须拒绝 .unwrap(),什么时候可以使用 .expect()?