【Rust自学】6.2. Option枚举

发布时间:2026/7/23 18:48:09
【Rust自学】6.2. Option枚举 6.2 Option枚举6.2.1. 什么是Option枚举它定义于标准库中并包含在prelude预导入模块里。它用来描述这样的场景某个值可能存在如果存在则是哪种数据类型或者它根本就不存在。6.2.2. Rust没有Null在大部分其他语言中都有Null这个值它代表没有值。在那些语言里一个变量可以处于两种状态- 空值Null- 非空Null的发明者托尼·霍尔Tony Hoare在2009年的演讲“Null References: The Billion Dollar Mistake”中说道I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.用中文说就是我称之为我的十亿美元错误。当时我正在设计第一个面向对象语言的综合引用类型系统。我的目标是确保所有引用的使用都绝对安全并由编译器自动执行检查。但我无法抗拒加入空引用的诱惑只是因为它很容易实现。这导致了无数的错误、漏洞和系统崩溃在过去四十年中可能造成了数十亿美元的痛苦和损失。Null的问题非常显而易见连其发明者都不认为这是个好东西。举个例子如果一个变量是字符串类型需要与另一个字符串拼接但这个变量实际上是Null那么在拼接时就会出错。对于Java用户来说最常见的错误就是NullPointerException。一句话总结当你尝试像使用非Null值那样使用Null值时就会引起某种错误。因此Rust没有提供Null。但是针对Null试图表达的概念——即某个值当前无效或由于某种原因不存在——Rust提供了一个类似的枚举叫做OptionT。6.2.3.OptionT它在标准库中的定义是这样的enum OptionT{ Some(T), None, }Some变体可以携带一些数据其数据类型就是T。T实际上是泛型参数以后会讲。None是另一个变体但它不携带任何数据因为它表示值不存在的情况。因为它包含在Prelude中所以可以直接使用OptionT、Some(T)和None。看个例子fn main(){ let some_number Some(5); let some_char Some(e); let absent_number: Optioni32 None; }对于前两个语句值都写在括号里了所以Rust编译器能够推断出其数据类型。例如some_number的类型是Optioni32some_char的类型是Optionchar。当然你也可以显式写出类型但没必要除非你想强制指定某个类型。对于最后一条语句赋的值是None变体。编译器无法根据None推断出OptionT中的T到底是什么类型所以需要显式声明具体类型。因此这里写的是Optioni32。在这个例子中前两个变量是有效值而最后一个变量不包含有效值。6.2.4.OptionT的优点在Rust里OptionT和TT可以是任何数据类型是不同的类型不能把OptionT当作T来用。若想使用OptionT中的T必须先把它转换成T。这避免了程序员忽略空值的可能性、直接操作可能为空的变量。Rust的OptionT设计迫使开发者显式处理这些情况。比如在C#中如果先写string a null;再写string b a 12345;却不检查a是否为空或者说忽略了a可能为空那么运行到第二行时就会出错。而在Rust里只要这个值的类型不是OptionT那么这个值就肯定不是空的。举个例子fn main(){ let x: i8 5; let y: Optioni8 Some(5); let sum x y; }如果运行这段代码编译器就会报错error[E0277]: cannot add Optioni8 to i8 -- src/main.rs:5:17 | 5 | let sum x y; | ^ no implementation for i8 Optioni8 | help: the trait AddOptioni8 is not implemented for i8 help: the following other types implement trait AddRhs -- /Users/stanyin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/arith.rs:98:9 | 98 | impl const Add for $t { | ^^^^^^^^^^^^^^^^^^^^^ i8 implements Add ... 113 | add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } | ---------------------------------------------------------------------------------- in this macro invocation | ::: /Users/stanyin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/internal_macros.rs:22:9 | 22 | impl const $imp$u for $t { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ i8 implements Addi8 ... 33 | impl const $imp$u for $t { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ i8 implements Addi8 ... 44 | impl const $imp$u for $t { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ i8 implements Add note: this error originates in the macro add_impl (in Nightly builds, run with -Z macro-backtrace for more info)报错的意思是无法把Optioni8和i8这两种类型相加因为它们不是同一种类型。那怎么让x和y相加呢很简单把y从Optioni8转换成i8即可fn main() { let x: i8 5; let y: Optioni8 Some(5); let sum match y { Some(value) x value, // 如果 y 是 Some则解包并相加 None x, // 如果 y 是 None则返回 x }; }