Module std::option
Option<T>是一个特殊的枚举,用来表示一个值可能有值,也可能为空
在其他的语言中,往往都会有一个null关键字,用来表明当前变量的值为空,但在rust中需要使用Option<T>来表示空值
Option有以下用途:
- 初始值
- 未在整个输入范围内定义的函数的返回值 (部分函数)
- 返回值,用于报告否则将报告简单错误的错误,其中错误返回
None - 可选的结构体字段
- 可借用或 “taken” 的结构体字段
- 可选的函数参数
- 可空指针
- 从困难的情况中交换东西
1.基本使用
Option<T> 枚举是如此有用以至于它被包含在了 prelude(prelude 属于 Rust 标准库,Rust 会将最常用的类型、函数等提前引入其中,省得我们再手动引入)之中,你不需要将其显式引入作用域。
另外,它的成员 Some 和 None 也是如此,无需使用 Option:: 前缀就可直接使用 Some 和 None。
比如下面一段代码
let maybe_number: Option<i32> = Option::Some(5);
let absent: Option<i32> = Option::None;完全可以去掉Option::,然后使用下面的方式
let maybe_number: Option<i32> = Some(5);
let absent: Option<i32> = None;注意
1.当使用Some(T)时,因为Some肯定是有值的,所以即使不显示声明类型,rust也会自动推导T的类型
如果使用 None 而不是 Some,需要告诉 Rust Option<T> 是什么类型的,因为编译器只通过 None 值无法推断出 Some 成员保存的值的类型。
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;2.Some(T)和T并不是一样的,两种类型不能混为一谈,比如下面这段代码
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;由于类型不同,所以不能相加,从而会报错
2.模式匹配
Option<T>既然是个枚举,那么就可以使用match进行模式匹配
let num = Some(66);
match num {
Some(value) => println!("{}", value),
None => (),
}当匹配到Some时,被匹配的变量的值就会赋值给Some()的参数,在Some后的{}块中就可以使用这个变量的值
2.1 作为函数返回值
Option<T>通常作为函数的返回值
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
fn main() {
let five = Some(5);
let six: Option<i32> = plus_one(five);
let none = plus_one(None);
println!("{:?}", six); // Some(6)
println!("{:?}", none); // None
}2.2 给外面变量赋值
Some(T)毕竟不能跟别的类型一块运算,但是可以通过match修改外部变量的值,从而进一步运算
let age = Some(19);
let is_adult = match age {
Some(value) => {
if value>=18 {true} else{false}
}
None => false,
};
println!("{}", is_adult); // true想取出Some(T)的值,也可以使用Option<T>的unwrap()方法,后面会有介绍
2.3 if let
if let是match的一个语法糖,常用于简化Option和Result的match语句先看如下代码
let num = Some(66);
match num {
Some(value) => println!("{}", value),
None => (),
}在很多时候如上面这样,我们只会关心当Option为Some时的情况,而None则一笔带过
if let可以让我们直接处理Some的情况,而不管None的情况,语法如下
let num=Some(123);
if let Some(value) = num {
println!("{}", value); // 123
}Some(value) = num 意思是判断变量num是否是一个Some,如果是Some,就把num的值赋值给value,可以在后面的语句块中访问这个value
3.Option和指针
Rust 的指针类型必须始终指向有效位置。没有 “null” 引用。相反,Rust 有 optional 指针,就像可选的拥有所有权的 box,Option<Box<T>>。
以下示例使用 Option 创建 i32 的可选 box。 注意,为了使用内部的 i32 值,check_optional 函数首先需要使用模式匹配来确定 box 是否有值 (即它是 Some(...)) 或没有 (None)。
let optional = None;
check_optional(optional);
let optional = Some(Box::new(9000));
check_optional(optional);
fn check_optional(optional: Option<Box<i32>>) {
match optional {
Some(p) => println!("has value {p}"),
None => println!("has no value"),
}
}4.问号运算符?
?运算符是一个错误传播运算符,它用于简化错误处理。
当一个返回Option的函数(暂称fn1)中使用了另一个返回Option的函数(暂称fn2),使用?运算符可以减少match语句的使用
- 如果
fn2返回的结果是Some,?会直接解包出内部的值,并继续执行 - 如果
fn2返回的结果是None,?会直接将整个函数返回None
当表达式返回Some时
fn fn1() -> Option<bool> {
return Some(true);
}
fn fn2() -> Option<bool> {
let is_true = fn1()?;
println!("{}",is_true); // true
if is_true { Some(true) } else { Some(false) }
}
fn main() {
let res = fn2();
println!("{:?}", res) // Some(true)
}当表达式返回None时
fn fn1() -> Option<bool> {
return None;
}
fn fn2() -> Option<bool> {
let is_true = fn1()?; // 直接返回None,后面代码不会执行
println!("{}", is_true);
if is_true { Some(true) } else { Some(false) }
}
fn main() {
let res = fn2();
println!("{:?}", res) // Some(true)
}5.Representation
Rust 保证优化以下 T 类型,以使 Option 具有与 T 相同的大小:
Box&U&mut Ufn,extern "C" fn1num::NonZero*ptr::NonNull#[repr(transparent)]结构体围绕此列表中的一种类型。
这称为 “空指针优化” 或 NPO。
对于上述情况,可以进一步保证,可以从 T 到 Option<T> 的所有有效值以及从 Some::<T>(_) 到 T 的所有有效值 mem::transmute (但是将 None::<T> 转换为 T 是未定义的行为)。
6.方法概述
除了使用模式匹配,Option 还提供了多种不同的方法。
6.1 查询变体
如果 Option 分别为 Some 或 None,则 is_some 和 is_none 方法返回 true。
6.2 用于处理引用的适配器
as_ref从&Option<T>转换为Option<&T>as_mut从&mut Option<T>转换为Option<&mut T>as_deref从&Option<T>转换为Option<&T::Target>as_deref_mut从&mut Option<T>转换为Option<&mut T::Target>as_pin_ref从Pin<&Option<T>>转换为Option<Pin<&T>>as_pin_mut从Pin<&mut Option<T>>转换为Option<Pin<&mut T>>
6.3 提取包含的值
当它是 Some 变体时,这些方法提取 Option 中包含的值。如果 Option 为 None:
expectpanics 带有提供的自定义消息unwrappanics 带有泛型信息unwrap_or返回提供的默认值unwrap_or_default返回类型T的默认值 (必须实现Defaulttrait)unwrap_or_else返回对提供的函数求值的结果
6.4 转换包含的值
ok_or使用提供的默认err值将Some(v)转换为Ok(v),将None转换为Err(err)ok_or_else使用提供的函数将Some(v)转换为Ok(v),并将None转换为Err的值transposetransposes anOptionof aResultinto aResultof anOption
这些方法转换了 Some 变体:
filtercalls the provided predicate function on the contained valuetif theOptionisSome(t), and returnsSome(t)if the function returnstrue; otherwise, returnsNoneflatten从一个对象中删除一层嵌套Option>map通过将提供的函数应用于Some的包含值并保持None值不变,将Option转换为Option
这些方法将 Option 转换为可能不同类型 U 的值:
map_or将提供的函数应用于Some的包含值,或者如果Option是返回提供的默认值Nonemap_or_elseapplies the provided function to the contained value ofSome, or returns the result of evaluating the provided fallback function if theOptionisNone
zipreturnsSome((s, o))ifselfisSome(s)and the providedOptionvalue isSome(o); otherwise, returnsNonezip_withcalls the provided functionfand returnsSome(f(s, o))ifselfisSome(s)and the providedOptionvalue isSome(o); otherwise, returnsNone
6.5 布尔运算符
这些方法将 Option 视为布尔值,其中 Some 的作用类似于 true,而 None 的作用类似于 false。这些方法有两类:一类以 Option 作为输入,一类以函数作为输入 (延迟评估)。
and、or 和 xor 方法将另一个 Option 作为输入,并生成一个 Option 作为输出。只有 and 方法可以生成具有与 Option 不同的内部类型 U 的 Option 值。
| method | self | input | output |
|---|---|---|---|
and | None | (ignored) | None |
and | Some(x) | None | None |
and | Some(x) | Some(y) | Some(y) |
or | None | None | None |
or | None | Some(y) | Some(y) |
or | Some(x) | (ignored) | Some(x) |
xor | None | None | None |
xor | None | Some(y) | Some(y) |
xor | Some(x) | None | Some(x) |
xor | Some(x) | Some(y) | None |
and_then 和 or_else 方法将函数作为输入,并且仅在需要产生新值时才评估函数。只有 and_then 方法可以生成具有与 Option 不同的内部类型 U 的 Option 值。
| method | self | function input | function result | output |
|---|---|---|---|---|
and_then | None | (not provided) | (not evaluated) | None |
and_then | Some(x) | x | None | None |
and_then | Some(x) | x | Some(y) | Some(y) |
or_else | None | (not provided) | None | None |
or_else | None | (not provided) | Some(y) | Some(y) |
or_else | Some(x) | (not provided) | (not evaluated) | Some(x) |
这是在方法调用管道中使用 and_then 和 or 等方法的示例。管道的早期阶段通过不变的失败值 (None),并继续处理成功值 (Some)。 最后,如果 or 收到 None,它会替换一条错误消息。
let mut bt = BTreeMap::new();
bt.insert(20u8, "foo");
bt.insert(42u8, "bar");
let res = [0u8, 1, 11, 200, 22]
.into_iter()
.map(|x| {
// `checked_sub()` 出错时返回 `None`
x.checked_sub(1)
// 与 `checked_mul()` 相同
.and_then(|x| x.checked_mul(2))
// `BTreeMap::get` 出错时返回 `None`
.and_then(|x| bt.get(&x))
// 如果到目前为止我们有 `None`,则替换一条错误消息
.or(Some(&"error!"))
.copied()
// 不会 panic 因为我们无条件使用了上面的 `Some`
.unwrap()
})
.collect::<Vec<_>>();
assert_eq!(res, ["error!", "error!", "foo", "error!", "bar"]);6.6 比较运算符
如果 T 实现 PartialOrd,那么 Option 将派生其 PartialOrd 实现。使用此顺序,None 的比较比任何 Some 都小,两个 Some 的比较方式与其在 T 中包含的值相同。 如果 T 也实现了 Ord,那么 Option 也是如此。
assert!(None < Some(0));
assert!(Some(0) < Some(1));6.7 迭代结束 Option
可以对 Option 进行迭代。如果您需要一个条件为空的迭代器,这会很有帮助。迭代器将产生单个值 (当 Option 为 Some 时),或不产生任何值 (当 Option 为 None 时)。 例如,如果 Option 是 Some(v),则 into_iter 的作用类似于 once(v); 如果 Option 是 None,则它的作用类似于 empty()。
Option 上的迭代器分为三种类型:
Option 上的迭代器在链接迭代器时很有用,例如,有条件地插入项。 (并不总是需要显式调用迭代器构造函数:许多接受其他迭代器的 Iterator 方法也将接受实现 IntoIterator 的可迭代类型,其中包括 Option。)
let yep = Some(42);
let nope = None;
// chain() 已经调用 into_iter(),所以我们不必这样做
let nums: Vec<i32> = (0..4).chain(yep).chain(4..8).collect();
assert_eq!(nums, [0, 1, 2, 3, 42, 4, 5, 6, 7]);
let nums: Vec<i32> = (0..4).chain(nope).chain(4..8).collect();
assert_eq!(nums, [0, 1, 2, 3, 4, 5, 6, 7]);以这种方式链接迭代器的一个原因是,返回 impl Iterator 的函数必须使所有可能的返回值都具有相同的具体类型。链接一个迭代的 Option 可以帮助解决这个问题。
fn make_iter(do_insert: bool) -> impl Iterator<Item = i32> {
// 显式返回来说明返回类型匹配
match do_insert {
true => return (0..4).chain(Some(42)).chain(4..8),
false => return (0..4).chain(None).chain(4..8),
}
}
println!("{:?}", make_iter(true).collect::<Vec<_>>());
println!("{:?}", make_iter(false).collect::<Vec<_>>());如果我们尝试做同样的事情,但是使用 once() 和 empty(),我们就不能再返回 impl Iterator,因为返回值的具体类型不同。
// 这不会编译,因为函数的所有可能返回必须具有相同的具体类型。
//
fn make_iter(do_insert: bool) -> impl Iterator<Item = i32> {
// 显式返回以说明返回类型不匹配
match do_insert {
true => return (0..4).chain(once(42)).chain(4..8),
false => return (0..4).chain(empty()).chain(4..8),
}
}6.8 收集到 Option
Option 实现了 FromIterator trait,它允许将 Option 值上的迭代器收集到原始 Option 值的每个包含值的集合的 Option 中,或者如果任何元素是 None,则为 None。
let v = [Some(2), Some(4), None, Some(8)];
let res: Option<Vec<_>> = v.into_iter().collect();
assert_eq!(res, None);
let v = [Some(2), Some(4), Some(8)];
let res: Option<Vec<_>> = v.into_iter().collect();
assert_eq!(res, Some(vec![2, 4, 8]));Option 还实现了 Product 和 Sum traits,允许对 Option 值的迭代器提供 product 和 sum 方法。
let v = [None, Some(1), Some(2), Some(3)];
let res: Option<i32> = v.into_iter().sum();
assert_eq!(res, None);
let v = [Some(1), Some(2), Some(21)];
let res: Option<i32> = v.into_iter().product();
assert_eq!(res, Some(42));6.9 就地修改 Option
这些方法返回对包含的值的可变引用 Option:
insert插入一个值,丢弃任何旧内容get_or_insertgets the current value, inserting a provided default value if it isNoneget_or_insert_default获取当前值,如果是,则插入类型T(必须实现Default) 的默认值Noneget_or_insert_withgets the current value, inserting a default computed by the provided function if it isNone
这些方法转移包含的值的所有权 Option:
taketakes ownership of the contained value of anOption, if any, replacing theOptionwithNonereplace获得Option包含的值的所有权 (如果有),用包含提供的值的Some替换Option
7.Examples
Option 上的基本模式匹配:
let msg = Some("howdy");
// 获取对所包含字符串的引用
if let Some(m) = &msg {
println!("{}", *m);
}
// 删除包含的字符串,销毁 Option
let unwrapped_msg = msg.unwrap_or("default message");循环前将结果初始化为 None:
enum Kingdom { Plant(u32, &'static str), Animal(u32, &'static str) }
// 要搜索的数据列表。
let all_the_big_things = [
Kingdom::Plant(250, "redwood"),
Kingdom::Plant(230, "noble fir"),
Kingdom::Plant(229, "sugar pine"),
Kingdom::Animal(25, "blue whale"),
Kingdom::Animal(19, "fin whale"),
Kingdom::Animal(15, "north pacific right whale"),
];
// 我们将搜索最大的动物的名称,但首先要获取 `None`。
//
let mut name_of_biggest_animal = None;
let mut size_of_biggest_animal = 0;
for big_thing in &all_the_big_things {
match *big_thing {
Kingdom::Animal(size, name) if size > size_of_biggest_animal => {
// 现在我们找到了一些大动物的名字
size_of_biggest_animal = size;
name_of_biggest_animal = Some(name);
}
Kingdom::Animal(..) | Kingdom::Plant(..) => ()
}
}
match name_of_biggest_animal {
Some(name) => println!("the biggest animal is {name}"),
None => println!("there are no animals :("),
}- 这对于任何其他 ABI 仍然适用:
extern "abi" fn(例如,extern "system" fn)
8.Structs
- IntoIter:对
Option的Some变体中的值的迭代器。 - Iter:对
Option的Some变体的引用的迭代器。 - IterMut:对
Option的Some变体的可变引用的迭代器。
