跳转到内容

Rust Option

Rust 没有 null。如果一个值可能不存在,就直接在类型里用 Option 表达出来。刚开始你会觉得多了些繁文缛节,但它彻底堵住了一整类 bug:在其他语言里导致过无数次崩溃的“空引用”。

Option 是一个有两个变体的枚举:

enum Option<T> {
Some(T), // 有值
None, // 没有值
}

其实你已经见过它了:vector 的 .get() 和 HashMap 的 .get() 返回的都是 Option。

fn main() {
let has_value: Option<i32> = Some(5);
let no_value: Option<i32> = None;
println!("{:?}", has_value);
println!("{:?}", no_value);
}

输出:

Some(5)
None
▶ 动手试一试

把值安全地取出来,靠的是 match。Rust 强制你处理两种情况,所以你不可能一不小心漏掉“值不存在”的情况:

fn main() {
let age: Option<i32> = Some(30);
match age {
Some(value) => println!("年龄是 {value}"),
None => println!("没有填写年龄"),
}
}
▶ 动手试一试
fn find_user(id: u32) -> Option<&'static str> {
if id == 1 {
Some("Alice")
} else {
None
}
}
fn main() {
match find_user(1) {
Some(name) => println!("找到了:{name}"),
None => println!("找不到这个用户"),
}
match find_user(99) {
Some(name) => println!("找到了:{name}"),
None => println!("找不到这个用户"),
}
}

输出:

找到了:Alice
找不到这个用户
▶ 动手试一试

找不到用户时,这个函数既不返回 null,也不抛出异常。它的返回类型本身就在告诉每个调用者:“这里可能什么也找不到。”而且编译器不会允许调用者忽略这种可能。

简单的场景下,写一整个 match 可能有点大材小用。这里有几个快捷写法:

fn main() {
let age: Option<i32> = None;
let value = age.unwrap_or(0); // 没有值时退回到 0
println!("{value}");
}
▶ 动手试一试

还有 .unwrap(),它直接把值给你,但如果是 None 就会 panic(程序崩溃):

let age: Option<i32> = Some(30);
let value = age.unwrap(); // 这里没问题,因为 age 是 Some

写一个函数 fn first_even(numbers: &[i32]) -> Option<i32>,返回 slice 中的第一个偶数,如果没有就返回 None。对 [3, 7, 8, 10] 用 match 打印结果,对 [1, 3, 5] 用 .unwrap_or(0) 打印结果。提示:遍历 slice,一旦 n % 2 == 0 就 return Some(n);循环结束后返回 None。

查看答案
fn first_even(numbers: &[i32]) -> Option<i32> {
for &n in numbers {
if n % 2 == 0 {
return Some(n);
}
}
None
}
fn main() {
match first_even(&[3, 7, 8, 10]) {
Some(n) => println!("第一个偶数:{n}"),
None => println!("没有偶数"),
}
let fallback = first_even(&[1, 3, 5]).unwrap_or(0);
println!("使用默认值:{fallback}");
}

输出:

第一个偶数:8
使用默认值:0
▶ 动手试一试

创建一个存放设置的 HashMap,里面只有 "volume",值为 80。然后遍历键 "volume" 和 "brightness":如果值存在就打印出来,不存在就打印一条“未设置”的提示。提示:HashMap 的 .get(key) 返回 Option,所以可以直接对它 match。

查看答案
use std::collections::HashMap;
fn main() {
let mut settings = HashMap::new();
settings.insert("volume", 80);
for key in ["volume", "brightness"] {
match settings.get(key) {
Some(value) => println!("{key} = {value}"),
None => println!("{key} 未设置"),
}
}
}

输出:

volume = 80
brightness 未设置
▶ 动手试一试
  • Option<T> 表示一个可能存在、也可能不存在的值:Rust 没有 null。
  • Some(value) 里装着值;None 表示什么都没有。
  • match 是处理这两种情况的安全方式,编译器不会让你漏掉其中任何一个。
  • .unwrap_or(default) 给你一个默认值;.unwrap() 遇到 None 会 panic,要少用。

小测验

1. 对于可能缺失的值,Rust 用什么来代替 null?

2. 对 None 调用 .unwrap() 会发生什么?

3. 当 age 是 None 时,age.unwrap_or(0) 会做什么?

4. 为什么说 match 是从 Option 中取值的安全方式?

5. 本页提到的哪些方法本身就返回 Option?

得分: 0 / 5