【Rust自学】6.2. Option枚举
2026/7/24 22:05:26 网站建设 项目流程

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提供了一个类似的枚举,叫做Option<T>

6.2.3.Option<T>

它在标准库中的定义是这样的:

enum Option<T>{ Some(T), None, }
  • Some变体可以携带一些数据,其数据类型就是T<T>实际上是泛型参数(以后会讲)。
  • None是另一个变体,但它不携带任何数据,因为它表示值不存在的情况。

因为它包含在Prelude中,所以可以直接使用Option<T>Some(T)None

看个例子:

fn main(){ let some_number = Some(5); let some_char = Some('e'); let absent_number: Option<i32> = None; }
  • 对于前两个语句,值都写在括号里了,所以Rust编译器能够推断出其数据类型。例如,some_number的类型是Option<i32>some_char的类型是Option<char>。当然你也可以显式写出类型,但没必要,除非你想强制指定某个类型。
  • 对于最后一条语句,赋的值是None变体。编译器无法根据None推断出Option<T>中的T到底是什么类型,所以需要显式声明具体类型。因此这里写的是Option<i32>

在这个例子中,前两个变量是有效值,而最后一个变量不包含有效值。

6.2.4.Option<T>的优点

  • 在Rust里,Option<T>TT可以是任何数据类型)是不同的类型,不能把Option<T>当作T来用。
  • 若想使用Option<T>中的T,必须先把它转换成T。这避免了程序员忽略空值的可能性、直接操作可能为空的变量。Rust的Option<T>设计迫使开发者显式处理这些情况。
    比如在C#中,如果先写string a = null;,再写string b = a + "12345";,却不检查a是否为空(或者说忽略了a可能为空),那么运行到第二行时就会出错。
    而在Rust里,只要这个值的类型不是Option<T>,那么这个值就肯定不是空的。

举个例子:

fn main(){ let x: i8 = 5; let y: Option<i8> = Some(5); let sum = x + y; }

如果运行这段代码,编译器就会报错:

error[E0277]: cannot add `Option<i8>` to `i8` --> src/main.rs:5:17 | 5 | let sum = x + y; | ^ no implementation for `i8 + Option<i8>` | = help: the trait `Add<Option<i8>>` is not implemented for `i8` help: the following other types implement trait `Add<Rhs>` --> /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 `Add<i8>` ... 33 | impl const $imp<&$u> for $t { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `i8` implements `Add<&i8>` ... 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)

报错的意思是:无法把Option<i8>i8这两种类型相加,因为它们不是同一种类型。

那怎么让xy相加呢?很简单,把yOption<i8>转换成i8即可:

fn main() { let x: i8 = 5; let y: Option<i8> = Some(5); let sum = match y { Some(value) => x + value, // 如果 y 是 Some,则解包并相加 None => x, // 如果 y 是 None,则返回 x }; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询