Rust 基本功 -- map 函数
Rust 基本功 -- map 函数
map 函数设计
rust 的 map() 函数设计原则有两种类别:
- eager(execute immediately): 调用 map 时立刻执行转换逻辑
- lazy(wait for use): 调用 map 时会返回一个包装类型, 不会立刻对内部的数据进行转换, 外部执行收集时才会转换.
| Type | Nature | Behavior |
|---|---|---|
Option<T> |
Eager | Transforms Some, ignores None. |
Result<T, E> |
Eager | Transforms Ok, ignores Err. |
Iterator |
Lazy | Returns a Map struct; code runs only during iteration. |
Ref / RefMut |
Eager | Transforms a reference to a field/sub-part. |
ControlFlow |
Eager | Transforms the Continue variant. |
一、Option::map (eager 类型)
Option::map 是 Rust 中处理 Option 类型最常用的方法之一,它允许我们在 Some 值上应用一个函数,同时保持 None 的情况不变。
1
2
3
4
5
6
7
8
9
10
11
impl<T> Option<T> {
pub fn map<U, F>(self, f: F) -> Option<U>
where
F: FnOnce(T) -> U,
{
match self {
Some(x) => Some(f(x)),
None => None,
}
}
}
关键点:
map接收self(按值),这意味着它会消费原始的Option<T>- 闭包
f接收T类型的所有权,使用FnOnce是因为值可能被移动 - 返回
Option<U>,其中U是闭包返回的类型 - 立即执行:
f(x)在map调用时立即执行,结果被包装在Some中
示例:
1
2
3
4
5
6
let opt = Some(5);
let result = opt.map(|x| {
println!("Processing {}", x); // 立即打印
x * 2
});
// 此时 result 已经是 Some(10),转换已经完成
二、Iterator::map (Lazy 类型)
Iterator::map 的设计与 Option::map 完全不同,它采用惰性求值(Lazy Evaluation)策略。
1
2
3
4
5
6
7
8
9
10
11
12
13
trait Iterator {
type Item;
fn map<B, F>(self, f: F) -> Map<Self, F>
where
Self: Sized,
F: FnMut(Self::Item) -> B,
{
Map::new(self, f)
}
fn next(&mut self) -> Option<Self::Item>;
}
关键点:
map返回一个新的迭代器适配器Map<Self, F>,不立即执行转换- 闭包
f使用FnMut,因为可能在多次迭代中被调用 - 实际的转换逻辑在调用
next()时才会执行
2.1 Map 适配器的内部实现
上面我们看到 map 函数调用之后返回了一个 Map<Self, F> 对象, 下面看看这个 Map 的内部逻辑是什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Clone)]
pub struct Map<I, F> {
// Used for `SplitWhitespace` and `SplitAsciiWhitespace` `as_str` methods
pub(crate) iter: I,
f: F,
}
impl<I, F> Map<I, F> {
pub(in crate::iter) fn new(iter: I, f: F) -> Map<I, F> {
Map { iter, f }
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<B, I: Iterator, F> Iterator for Map<I, F>
where
F: FnMut(I::Item) -> B,
{
type Item = B;
#[inline]
fn next(&mut self) -> Option<B> {
// 关键:只有在调用 next() 时才执行闭包
self.iter.next().map(&mut self.f)
}
// ...
}
有趣的设计细节:
#[must_use]属性:提醒开发者迭代器是惰性的,必须被消费才会执行Map结构体只是包装了原始迭代器和闭包,不存储任何转换结果- 每次
next()调用时,才从底层迭代器获取一个元素并应用闭包
2.2 惰性求值的实际表现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let vec = vec![1, 2, 3, 4, 5];
// 创建迭代器链,但此时没有任何计算发生
let mapped = vec.iter()
.map(|x| {
println!("Processing {}", x); // 这行代码还没有执行!
x * 2
})
.map(|x| {
println!("Doubling {}", x); // 这行代码也没有执行!
x * 2
});
// 即使创建了多个 map,仍然没有任何输出
println!("Iterator created, but no processing yet");
// 只有在消费迭代器时,闭包才会执行
for value in mapped {
println!("Got: {}", value);
}
// 输出:
// Processing 1
// Doubling 2
// Got: 4
// Processing 2
// Doubling 4
// Got: 8
// ...
This post is licensed under
CC BY 4.0
by the author.