设计哲学

将可能性编码到类型系统中:Rust 的 ResultOption 类型允许在编译时捕获错误和空值,避免运行时的「空指针」调用。

在很多语言中,null 的存在带来了大量的运行时错误(比如著名的 NullPointerException)。函数返回一个特殊值(如 -1)来表示错误,也常常导致开发者忘记检查从而引发 bug。

Rust 通过 Option 和 Result<T, E> 在编译时就解决了这些问题。它们将「可能不存在的值」和「可能失败的操作」这两种情况明确地编码到类型里,强迫开发者在编译阶段就处理这些可能性,从而极大地提升了代码的健壮性。

  • Option<T> 代表一个值可能存在,也可能不存在。

  • Result<T, E> 代表一个操作可能成功,也可能失败。

Option

Option<T> 是一个枚举(Enum),它有两个变体:

  • Some(T): 表示值存在,T 就是那个具体的值。

  • None: 表示值不存在。

Result

Result<T, E> 也是一个枚举,它有两个变体:

  • Ok(T): 操作成功,T 是成功的值。

  • Err(E): 操作失败,E 是错误信息。

链式调用

这里把 Result 和 Option 放到一起说,因为大多数链式调用从语义上是相同的,一起说更容易理解。至于有一些 Option 或 Result 独有的方法则单独拎出来说。

下面把 None 和 Err 统称为「无值「,Some 和 Ok 统称为「有值」,Result 和 Option 统称为容器类型,原值类型为 T,新值类型为 U。下面会根据我认为的语义来把链式调用分为几个类:

无值则 Panic!

此类方法在无值时会直接 panic,通常用于调试或开发者确认某个值一定存在的场景。

方法 Result 签名 Option 签名 描述
unwrap fn(self) -> T fn(self) -> T 获取 OptionResult 中的值,如果值不存在或操作失败,则会 panic。
expect fn(self, msg: &str) -> T fn(self, msg: &str) -> T 类似于 unwrap,但可以提供自定义的错误信息。
unwrap_or fn(self, default: T) -> T fn(self, default: T) -> T 获取值,如果无值返回提供的默认值。
unwrap_or_else fn(self, op: F) -> T fn(self, op: F) -> T 获取值,如果无值调用闭包返回默认值。
unwrap_or_default fn(self) -> E N / A 获取值,如果无值返回类型的默认值。

转换 - Mapping

此类方法用于将值从一个类型 T 转换成另外一个类型 U。不同之处仅在于当无值时的处理方式。

方法 Result 签名 Option 签名 描述
map fn(self, op: F) -> Option fn(self, op: F) -> Option 如果有值,则将内部值 T 通过闭包转换 U。返回类型为容器。
map_or fn(self, default: T, op: F) -> T fn(self, default: U, op: F) -> U 如果有值,则 T 转 U,如果无值,则返回默认值 U,返回类型为 U。
map_or_else fn(self, default: F, op: F) -> T fn(self, default: F, op: F) -> T 如果有值,则 T 转 U,如果无值,则调用另一个闭包返回 U,返回类型为 U。

链式调用

此类方法类似于 if 条件分支,可以针对有值或无值的情况继续写逻辑从而避免大量的类似 if Some(T) = F() { ... } 这样的条件判断。

方法 Result 签名 Option 签名 描述
and fn(self, res: Result<U, E>) -> Result<U, E> fn(self, opt: Option) -> Option 如果有值,则返回另一个容器类型,否则返回无值。
and_then fn(self, op: F) -> Result<U, E> fn(self, op: F) -> Option 如果有值,则将内部值 T 通过闭包转换 U,返回类型为容器。无值则继续返回无值容器
or fn(self, res: Result <U, E>) -> Result<T, E> fn(self, opt: Option) -> Option 如果有值,则返回当前容器类型,否则返回另一个容器类型。
or_else fn(self, op: F) -> Result<T, E> fn(self, op: F) -> Option 如果有值,则返回当前容器类型,否则调用闭包返回另一个容器类型。
filter fn(self, op: F) -> Result<T, E> fn(self, op: F) -> Option 如果有值且满足条件,则返回当前容器类型,否则返回无值。

Option Result 互转

有时我们需要将 OptionResult 互相转换,这些方法可以帮助我们完成这种转换。

方法 Result 签名 Option 签名 描述
ok fn(self) -> Option N / A Result 转换为 Option,如果是 Ok 则返回 Some(T),否则返回 None
err fn(self) -> Option N / A Result 转换为 Option,如果是 Err 则返回 Some(E),否则返回 None
ok_or fn(self, default: T) -> Result<T, E> N / A Option 转换为 Result,如果是 Some(T) 则返回 Ok(T),否则返回 Err(default)
ok_or_else fn(self, op: F) -> Result<T, E> N / A Option 转换为 Result,如果是 Some(T) 则返回 Ok(T),否则调用闭包返回 Err

总结

传统的错误处理(如 if err != nil)是一种「控制流」思想。你需要在代码的 Happy Path(成功路径)中穿插各种错误检查的分支。

Rust 的 OptionResult 及其组合子(combinators, 如 map, and_then)鼓励你用「数据流」的思维来编程。你可以想象数据在一个管道里流动,这个管道有两个轨道:Some/Ok 轨道和 None/Err 轨道。

map, and_then 等操作是在 Some/Ok 轨道上对数据进行加工。

一旦任何一个环节出错,数据就会切换到 None/Err 轨道,并一直走到终点。

or_else 像是提供了一个从 None/Err 轨道切换回 Some/Ok 轨道的机会。

这种模型(有时被称为「铁路导向编程」)让你的主逻辑非常清晰,错误处理被优雅地链接在一起,而不是打断主逻辑。