Rust中的关键字Some、Option

作者: adm 分类: Rust 发布时间: 2024-07-14

Rust中的关键字Some、Option
根据Rust本身的设计哲学, 建议在设计某个变量时, 如果预计该变量某时刻可能会是空值(null/None)的话, 那么尽量用Option/Result来包裹它, 反过来说, 只有你可以肯定该变量不可能为空值时, 才无须这么搞.

rust为了处理情况设置的两个枚举类型,分别是enum Option 和enum Result。

Option的枚举情况有两种,分别是代表有的Some()和代表无的None。 如果是有返回值,则可以通过if let,match,unwrap,?等多种方法对应情况取出Some包裹的值,如果没有则是None。

Result的枚举情况也是有两种,表示正确的Ok()和表示错误的Err()。同样也是match,unwrap等等对应方法去提取。分别提取对应情况的内容。

在rust的设计中,Option的设计非常巧妙,避免了其它语言中常见的Null引起的各种错误和异常。

关于枚举
Rust 语言提供了 enum 关键字用于定义枚举。

定义枚举的语法格式如下

enum enum_name {
   variant1,
   variant2,
   variant3
}

Option
严格的来说,只有 Option 和 Result,Some 只是 Option 的一个值包装类型。
Option类型代表了一个可选的值,每个Option要么是一个Some中包含一个值,要么是一个None。Option的定义如下:

pub enum Option {
    None,
    Some(T),
}

Option主要有以下一些用法:

初始化值;
作为在整个输入范围内没有定义的函数的返回值;
作为返回值,用None表示出现的简单错误;
作为结构体的可选字段;
作为结构体中可借出或者是可载入的字段;
作为函数的可选参数;
代表空指针;
用作复杂情况的返回值。
我们先来描述一个普通的场景,在很多语言中,获取一个数据可能会返回 null 也可能会没有数据,拿 java 举个例:

User user = model.getUser();

if (user == null) {
    throw new Exception("用户不存在");
}

System.out.println("你好:" + user.getName());

我们在拿到一个不确定,可能为空的值时,为了确保不出现 NullPointerException 的问题,需要先判断值是否是 null。

这个看起来没什么问题,不过仔细思考,实际上会有两个问题:

开发者极容易忘记做 null 的判断,实际开发和测试中也许不容易碰到为 null 的情况,导致线上运行报错。
即使只是为 null 时简单的抛异常,代码也显的有点啰嗦,这种简单的判断能不能少点代码?
Rust 的 Option 就解决了这个问题。
Option 本质上是一个枚举,有两种分别是Some 和 None。Some 代表有值,None 则类似于 null,代表无值。

为什么这么搞呢?
Option和T不是同一个类, 不要小看着一个简单的区别,这意味着Option和T是不能直接进行运算的,即Option和i32是不能直接相加的。实际上,更进一步的,在对Option进行T的运算时,必须先将Option转化成T类型。如此一来就可以帮助我们避免以为值非空而实际为空的情况。

例如:

fn main() {
    let a: i32 = 1;
    let b: Option = Some(5);
    let c = a + b;
    println!("{}", c);
}

编译器会报如下错误,我们必须先将b从Option类型转化为i32才能进行i32类型的运算。如下

fn main() {
    let a: i32 = 1;
    let b: Option = Some(5);
    let c = a + b.unwrap();
    println!("{}", c);
}

每当我们引入一个可能为空的值时,我们必须先把它放到Option里。当我们使用这个值时,我们必须先处理值为空的情况。也就是说,只要一个值不是Option类型的,我们就可以认定它的值不为空。

Rust在写代码和编译时就发现所有的错误,而不是运行时才发现。理解了这一点,你就能理解 Rust 的各种设定。

Some使用举例

let some_value = Some(10);
match some_value {
	Some(3) => println!("three"),
	_ => println! (“other”),
}

但是,当我们只关心等于3时的情况,用match就感觉代码太多了,那么我们就可以使用if let:

if let Some(3) = some_u8_value {
	println!("three");
} else {
	println! (“other”);
}

Option 类型的unwrap方法
在确认Option不为None的情况下,可以用unwrap方法拆解出其中的值,并获取值的所有权。这里要强调的是,unwrap会消费Option本身的值,后面就不能再用了。

Option 类型的 is_none 方法
这个方法用于判断 Option 是否为 None

Option 类型的take 方法

 
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_option", issue = "67441")]
pub const fn take(&mut self) -> Option {
    // FIXME replace `mem::replace` by `mem::take` when the latter is const ready
    mem::replace(self, None)
}

这个方法的作用是取出可变引用所指向的值并将其替换为 None,然后返回原来的值作为 Option。这在某些情况下可能会很有用,比如需要将值从某个地方取出并标记为已取出的情况。

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!