这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战
option<T>
前面的文章中我们介绍了enum
的简单使用,本篇文章主要介绍option
,option
是标准库定义的另一个枚举,option
的应用更加广泛,原因是它设定了一个非常普遍的场景,即要么有值要么没值。
从类型系统的角度来表达这个概念就意味着编译器在编译时就需要检查是否处理了所有应该处理的情况,这样就可以避免在其他编程语言中非常常见的 bug。
Rust语言并没有很多其他语言中有的空值功能。空值(Null )是一个值,它代表没有值。在有空值的语言中,变量总是这两种状态之一:空值和非空值。
空值的问题在于当你尝试像一个非空值那样使用一个空值,会出现某种形式的错误。因为空和非空的属性无处不在,非常容易出现这类错误。
而Rust从根本上解决了这个问题,因为Rust没有空值,但是Rust拥有一个可以编码存在或不存在概念的枚举。这个枚举就是option
,而且option
定义于标准库中,如下代码示例:
enum Option<T> {
some(T),
None
}
复制代码
<T>
语法会在后续文章中介绍,它是一个泛型类型参数,option<T>
是常规的枚举,Some(T)
和 None
是 Option<T>
的成员。
在本文中只需要知道的是 <T>
意味着 Option
枚举的 Some
成员可以包含任意类型的数据。如下述代码示例:
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
}
复制代码
如果使用 None
而不是 Some
,需要告诉 Rust Option<T>
是什么类型的,因为编译器只通过 None
值无法推断出 Some
成员保存的值的类型。
当有一个 Some
值时,我们就知道存在一个值,而这个值保存在 Some
中。当有个 None
值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。那么,Option<T>
为什么就比空值要好呢?简而言之,因为 Option<T>
和 T
(这里 T
可以是任何类型)是不同的类型,编译器不允许一个像肯定非空的值和肯定非空的值进行比较或者运算,比如下述代码示例:
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
}
复制代码
如果运行上述代码,则会抛出下面的异常:
error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is
not satisfied
-->
|
5 | let sum = x + y;
| ^ no implementation for `i8 + std::option::Option<i8>`
|
复制代码
异常的意思就是Rust 不知道该如何将 Option<i8>
与 i8
相加,因为它们的类型不同。当在 Rust 中拥有一个像 i8
这样类型的值时,编译器确认为它总是有一个有效的值。我们无需任何顾虑自信使用而无需做空值检查。只有当使用 Option<i8>
(或者任何用到的类型)的时候需要担心可能没有值。
换句话说,在对 Option<T>
进行 T
的运算之前必须将其转换为 T
。通常帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。
如果程序中可能会出现值为空的值,就必须现实的将其放入对应类型的 Option<T>
中。当使用这个值时,必须明确的处理值为空的情况。这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。
结语
文章首发于微信公众号程序媛小庄,同步于掘金。
码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)