291 lines
7.2 KiB
Markdown
291 lines
7.2 KiB
Markdown
非常好的问题 👏
|
||
`Option<&V>` 是 Rust 中非常核心的类型之一,理解它对你掌握 **Rust 的所有权系统** 和 **安全编程哲学** 都至关重要。
|
||
我们来一步步深入讲清楚,从原理到底层实现、再到各种用法与技巧。
|
||
|
||
---
|
||
|
||
# 🧩 一、什么是 `Option<T>`
|
||
|
||
## ✅ 定义
|
||
|
||
`Option<T>` 是一个**枚举类型**,定义在标准库中:
|
||
|
||
```rust
|
||
enum Option<T> {
|
||
Some(T),
|
||
None,
|
||
}
|
||
```
|
||
|
||
也就是说,它是一个「可能有值,也可能没有值」的容器。
|
||
|
||
* `Some(T)` 表示存在一个值;
|
||
* `None` 表示值不存在。
|
||
|
||
---
|
||
|
||
## 🧠 为什么要有 `Option<T>`
|
||
|
||
在很多语言(如 C/C++、Java)中,缺失值通常用 `NULL` 或 `None`,但容易出错。
|
||
Rust 用 `Option<T>` 代替「空指针」,**在类型层面上强制你处理缺失的情况**。
|
||
这样编译器就能帮你防止空指针错误(`null pointer dereference`)。
|
||
|
||
---
|
||
|
||
# 🔍 二、`Option<&V>` 是什么?
|
||
|
||
在 `HashMap::get()` 等函数中,你会看到返回类型是:
|
||
|
||
```rust
|
||
Option<&V>
|
||
```
|
||
|
||
意思是:
|
||
|
||
> “可能返回一个指向值 `V` 的引用,如果 key 不存在,则返回 None。”
|
||
|
||
举个例子:
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
|
||
fn main() {
|
||
let mut map = HashMap::new();
|
||
map.insert("apple", 3);
|
||
|
||
let v1 = map.get("apple"); // Some(&3)
|
||
let v2 = map.get("banana"); // None
|
||
|
||
println!("{:?} {:?}", v1, v2);
|
||
}
|
||
```
|
||
|
||
输出:
|
||
|
||
```
|
||
Some(3) None
|
||
```
|
||
|
||
---
|
||
|
||
# 🧩 三、匹配(模式匹配)方式
|
||
|
||
最常见的处理方式是 **match**:
|
||
|
||
```rust
|
||
match map.get("apple") {
|
||
Some(value) => println!("Found: {}", value),
|
||
None => println!("Not found"),
|
||
}
|
||
```
|
||
|
||
这种写法非常直观,但略显冗长,于是 Rust 提供了更优雅的写法👇
|
||
|
||
---
|
||
|
||
# ⚙️ 四、常见用法大全
|
||
|
||
## 1️⃣ `is_some()` / `is_none()`
|
||
|
||
判断是否存在值:
|
||
|
||
```rust
|
||
if map.get("apple").is_some() {
|
||
println!("存在 apple");
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2️⃣ `unwrap()` / `unwrap_or()` / `unwrap_or_default()`
|
||
|
||
提取值:
|
||
|
||
```rust
|
||
let v = map.get("apple").unwrap(); // 若 None 则 panic!
|
||
let v = map.get("apple").unwrap_or(&0); // 若 None 则返回 &0
|
||
let v = map.get("apple").unwrap_or_default(); // 若 None 则返回默认值
|
||
```
|
||
|
||
> ⚠️ 不建议随意使用 `unwrap()`,除非你确信一定有值。
|
||
|
||
---
|
||
|
||
## 3️⃣ `if let` 简写匹配
|
||
|
||
```rust
|
||
if let Some(v) = map.get("apple") {
|
||
println!("数量: {}", v);
|
||
}
|
||
```
|
||
|
||
比 `match` 简洁,常用于只关心 `Some` 的情况。
|
||
|
||
---
|
||
|
||
## 4️⃣ `map()` —— 映射转换
|
||
|
||
`map()` 能把 `Option` 内的值加工成另一个类型:
|
||
|
||
```rust
|
||
let length = map.get("apple").map(|v| v * 2);
|
||
println!("{:?}", length); // Some(6)
|
||
```
|
||
|
||
若为 `None`,则结果仍是 `None`。
|
||
|
||
---
|
||
|
||
## 5️⃣ `and_then()` —— 链式调用
|
||
|
||
用于多层 Option 嵌套场景:
|
||
|
||
```rust
|
||
let result = map.get("apple")
|
||
.and_then(|v| Some(v * 10))
|
||
.and_then(|x| Some(x + 1));
|
||
println!("{:?}", result); // Some(31)
|
||
```
|
||
|
||
---
|
||
|
||
## 6️⃣ `unwrap_or_else()` —— 延迟计算默认值
|
||
|
||
```rust
|
||
let v = map.get("banana").unwrap_or_else(|| {
|
||
println!("key 不存在,使用默认值");
|
||
&0
|
||
});
|
||
```
|
||
|
||
如果 key 不存在,执行闭包生成默认值。
|
||
|
||
---
|
||
|
||
## 7️⃣ `as_ref()` / `as_mut()`
|
||
|
||
将 `Option<T>` 转换为 `Option<&T>` 或 `Option<&mut T>`:
|
||
|
||
```rust
|
||
let opt = Some(String::from("hi"));
|
||
let ref_opt = opt.as_ref(); // Option<&String>
|
||
let mut_opt = opt.as_ref(); // Option<&mut String>
|
||
```
|
||
|
||
这在借用时非常实用,尤其是嵌套结构。
|
||
|
||
---
|
||
|
||
## 8️⃣ `expect()` —— 自定义 panic 信息
|
||
|
||
```rust
|
||
let v = map.get("apple").expect("apple 不存在!");
|
||
```
|
||
|
||
如果是 `None`,会 panic 并显示 `"apple 不存在!"`。
|
||
|
||
---
|
||
|
||
## 9️⃣ `copied()` / `cloned()`
|
||
|
||
如果你要从 `Option<&T>` 提取出 `Option<T>`(复制/克隆),可以这样:
|
||
|
||
```rust
|
||
let map = HashMap::from([("a", 10)]);
|
||
let val = map.get("a").copied(); // Option<i32>
|
||
println!("{:?}", val); // Some(10)
|
||
```
|
||
|
||
区别:
|
||
|
||
* `.copied()` 要求 `T: Copy`
|
||
* `.cloned()` 要求 `T: Clone`
|
||
|
||
---
|
||
|
||
## 🔟 `unwrap_or_default()`
|
||
|
||
对简单类型(如 `i32`、`bool`)非常方便:
|
||
|
||
```rust
|
||
let map = HashMap::new();
|
||
let v: i32 = *map.get("x").unwrap_or_default(); // None -> 默认0
|
||
```
|
||
|
||
---
|
||
|
||
# 🧠 五、内部机制(零成本抽象)
|
||
|
||
Rust 的 `Option<T>` 在底层通常会被 **编译器优化为“无额外开销”** 的形式。
|
||
|
||
举个例子:
|
||
`Option<&T>` 通常和 `*const T` 的大小完全一样。
|
||
因为:
|
||
|
||
* `None` 会用空指针 `0` 表示;
|
||
* `Some(x)` 则用非零指针。
|
||
|
||
所以:
|
||
|
||
```rust
|
||
std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>()
|
||
```
|
||
|
||
✅ 成立,说明 `Option<&T>` 没有额外开销。
|
||
|
||
---
|
||
|
||
# 🧩 六、总结表格
|
||
|
||
| 方法 | 作用 | 示例 | | |
|
||
| --------------------- | -------------------------------- | ------------------------- | - | ----------- |
|
||
| `is_some()` | 是否有值 | `opt.is_some()` | | |
|
||
| `is_none()` | 是否为空 | `opt.is_none()` | | |
|
||
| `unwrap()` | 直接取值(危险) | `opt.unwrap()` | | |
|
||
| `unwrap_or(x)` | 无值时返回 x | `opt.unwrap_or(0)` | | |
|
||
| `unwrap_or_default()` | 无值时返回默认值 | `opt.unwrap_or_default()` | | |
|
||
| `unwrap_or_else(f)` | 无值时调用函数生成 | `opt.unwrap_or_else( | | 0)` |
|
||
| `expect(msg)` | 无值时 panic,带信息 | `opt.expect("错误")` | | |
|
||
| `map(f)` | 处理 Some 中的值 | `opt.map( | x | x+1)` |
|
||
| `and_then(f)` | 链式 Option 处理 | `opt.and_then( | x | Some(x+1))` |
|
||
| `as_ref()` | 转换为 Option<&T> | `opt.as_ref()` | | |
|
||
| `as_mut()` | 转换为 Option<&mut T> | `opt.as_mut()` | | |
|
||
| `copied()` | Option<&T> → Option<T>(T: Copy) | `opt.copied()` | | |
|
||
| `cloned()` | Option<&T> → Option<T>(T: Clone) | `opt.cloned()` | | |
|
||
|
||
---
|
||
|
||
# 🔚 七、一个综合示例
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
|
||
fn main() {
|
||
let mut scores = HashMap::new();
|
||
scores.insert("Alice", 90);
|
||
scores.insert("Bob", 85);
|
||
|
||
let name = "Charlie";
|
||
let score = scores.get(name)
|
||
.copied() // Option<i32>
|
||
.unwrap_or_else(|| {
|
||
println!("{name} 不存在,使用默认分数");
|
||
60
|
||
});
|
||
|
||
println!("{name} 的分数是 {score}");
|
||
}
|
||
```
|
||
|
||
输出:
|
||
|
||
```
|
||
Charlie 不存在,使用默认分数
|
||
Charlie 的分数是 60
|
||
```
|
||
|
||
---
|
||
|
||
是否希望我接着帮你画一张「`Option<T>` 方法调用关系图(功能导图)」?
|
||
那张图能直观看出各种方法如何互相转换,非常适合记忆和快速查阅。
|