一句话总结
Rust 的枚举(enum)远比 Go 的 iota 常量强大:每个变体可以携带不同类型的数据,是"代数数据类型".
最重要的应用是 Option--它彻底取代了 nil/null,把"可能没有值"编码进类型,让空指针错误在编译期消失.
枚举:不只是一组常量
最简单的枚举像 Go 的常量集合:
|
但 Rust 枚举的真正威力在于:每个变体可以携带数据,而且各变体携带的数据类型可以不同.
|
这意味着一个 Message 值"要么是 Quit,要么是带坐标的 Move,要么是带字符串的 Write...",且类型系统强制处理每一种可能.
Go 里要表达"带数据的多态状态",通常得用 interface{} + 类型断言,既啰嗦又容易漏分支.
Rust 的枚举把所有可能性收拢进一个类型,编译器还会确保处理了每个变体--这是 Rust 类型系统最受称道的特性之一.
用枚举为"状态"建模
枚举特别适合表达"有限的几种状态/形态".比起用多个布尔标志或整数常量去拼,枚举让非法状态根本无法表示:
|
最佳实践:让非法状态无法表示
"Make illegal states unrepresentable" 是 Rust 建模的核心思想.当几种状态互斥,且各自附带不同数据时,用枚举而非一堆布尔/可选字段.
这样编译器替排除了自相矛盾的组合.Go 缺少携带数据的枚举,这类约束只能靠运行时检查和约定,Rust 把它前移到了类型层面.
给枚举实现方法
和结构体一样,枚举也能有 impl 块和方法:
|
Option:Rust 没有 null
枚举最重要的应用,是用标准库的 Option<T> 取代 nil:
|
Rust 没有 null/nil.一个可能"没有值"的结果,类型就是 Option<T>,而编译器强制在取出值之前处理 None 的情况--无法像 Go 那样不小心对 nil 解引用:
|
Go 里"未找到"常用返回 nil 或 (value, ok),是否检查全靠自觉,漏检就 nil panic.
Rust 的 Option 把"可能没有"写进类型签名,不处理 None 就编译不过--空指针这个"十亿美元的错误"在 Rust 里被类型系统消灭了.
Option 的常用方法:别动不动就 match
每次都写完整 match 太啰嗦.Option 提供了大量便捷方法,熟练使用它们是写出地道 Rust 的标志:
| 方法 | 作用 |
|---|---|
unwrap_or(default) |
有值取值,None 则用默认值 |
unwrap_or_else(|| ...) |
同上,默认值由闭包惰性计算 |
map(|v| ...) |
有值则变换,None 保持 None |
and_then(|v| ...) |
链式,闭包返回另一个 Option(扁平化) |
is_some() / is_none() |
判断是否有值 |
ok_or(err) |
把 Option 转成 Result(第 14 篇用) |
|
最佳实践:用组合子代替手写判空
遇到 Option,先想 map/and_then/unwrap_or 这类"组合子(combinator)"能不能优雅表达,而不是一上来就 match 或 if let.这让代码更紧凑,意图更清晰.
当然,需要对两个分支都做复杂处理时,match 仍是最清楚的选择--下一篇专门讲它.
unwrap 是'我保证有值'的承诺,慎用
option.unwrap() 在 None 时会 panic.它适合原型,测试或能逻辑确证不为 None 的场景,但在生产业务路径上滥用 unwrap 等于放弃了 Option 的保护.
优先用 unwrap_or 等给出兜底,或用 ?(第 14 篇)向上传播.
承上启下
Option 和带数据的枚举都依赖一个强大工具来"拆开"--match 模式匹配.下一篇就专门讲它,以及如何优雅,穷尽地处理这些代数类型.
快速回顾
- 枚举携带数据:每个变体可带不同类型的数据,是代数数据类型,远强于 Go 的 iota 常量.
- 状态建模:用枚举让互斥状态各自携带恰当数据,使"非法状态无法表示".
- 枚举有方法:同结构体一样可写
impl块. - Option 取代 nil:
Some(T)/None,编译器强制处理 None,消灭空指针错误. - 组合子优先:用
map/and_then/unwrap_or等代替手写判空,更地道. - 慎用 unwrap:None 时 panic,生产路径优先用兜底或
?传播.
动手练习
- 枚举面积:定义一个
enum Shape,含Circle(f64)和Rectangle(f64, f64),并为它实现一个area(&self)方法. - 状态建模:用枚举为"红绿灯"建模(红/黄/绿三态),思考它比用三个布尔字段好在哪.
- Option 处理:写一个返回
Option<f64>的除法函数(除数为 0 返回 None),分别用match和unwrap_or处理结果. - 链式组合子:给一个
Option<i32>用map+filter+unwrap_or链式处理--有值则平方,仅保留大于 10 的,否则得 0. - 非法状态消除:把"用多个布尔字段表达连接状态"改写成一个枚举,体会"非法状态无法表示".