Rust 迭代器多级链式调用执行顺序笔记
Rust迭代器链式调用执行顺序核心真相
不是先把第一级全部跑完生成全部中间数据,再跑下一级;而是逐个元素,一个元素流经整条链的所有适配器。惰性求值。
核心结论
- 构造阶段 :
source.map().filter()只是组装嵌套迭代器结构体,不会处理任何元素,惰性。- 执行阶段 :下游主动向上游拉取数据(Pull‑based 拉模式);单个元素依次流经整条适配器链,不是先完整跑完一级再跑下一级。
- 不会自动生成中间容器;只有手动
collect()才会产生中间集合。
演示代码:直观看执行顺序
rust
vec![1,2,3]
.iter()
.map(|x| {println!("map {}",x); *x})
.filter(|v| {println!("filter {}",v); *v>1})
.for_each(|v| println!("消费 {}", v));
输出:
txt
map 1
filter 1
map 2
filter 2
消费 2
map 3
filter 3
消费 3
- 看输出:1完整走完 map → filter;然后2完整走完 map→filter;再3完整走完map→filter。
不是:全部先map一遍输出 map1 map2 map3 ,再全部filter一遍。
两个概念必须分清(极易混淆)
1. 构造链式:从左到右,一次性把迭代器结构体组装完成
rust
let chain = vec![1,2,3].iter().map(...).filter(...);
这行代码没有做任何元素处理!
只是构造嵌套结构体: Filter<Map<Iter<...>, F1>, F2>
.map() 返回Map结构体;拿这个Map作为入参调用 .filter() ,返回Filter结构体。
✅ 构造阶段:完整生成迭代器链式结构,但是零元素计算。
2. 执行阶段(调用 next() / for_each / collect):逐个元素,流水式穿过整条链
当开始消费的时候,才真正跑逻辑。调用 chain.next() :
txt
Filter.next()
→ Map.next()
→ Iter.next() 取出一个元素
→ 执行map闭包转换
→ 拿到map输出值,执行filter判断
→ 如果true,返回Some(值);false继续循环调用上游next()
📌 一句话区分:
- 构造阶段:方法调用顺序: iter → map → filter ,生成嵌套结构体,没有数据处理。
- 执行阶段:每个元素:上游产出1个元素,依次流过map、filter适配器;处理完这一个,再处理下一个。不会生成中间容器,不会把map全部算完存起来。
很多人误区:以为 .map().filter() 等价于:
rust
// ❌ 不是这样!会分配中间Vec
let temp:Vec<_> = vec.iter().map(...).collect();
temp.filter(...).xxx;
上面写法会产生中间集合;迭代器链式不会产生中间集合。
拆解适配器内部 next() 伪代码,理解调用栈
简化结构体:
rust
struct Map<I,F> { iter:I, f:F }
struct Filter<I,P> { iter:I, p:P }
Map 的 next
impl<I,F,B> Iterator for Map<I,F>
where I:Iterator, F:FnMut(I::Item)->B
{
fn next(&mut self) -> Option<B> {
// 调用上游迭代器next拿一个元素
self.iter.next().map(&mut self.f)
}
}
Filter 的 next(重点!filter内部循环)
impl<I,P> Iterator for Filter<I,P>
where
I:Iterator,
P:FnMut(&I::Item)->bool
{
fn next(&mut self) -> Option<I::Item> {
self.iter.find(&mut self.predicate)
}
}
现在链式: Filter { iter: Map{ iter: Iter, f:map_fn }, p:filter_fn }
调用 filter.next() 的完整执行流程:
- Filter.next() 进入loop
- 调用 self.iter.next() → 也就是 Map.next()
- Map.next() 调用 self.iter.next() → Iter.next() ,得到 Some(&1)
- Map执行map闭包,得到 1 返回 Some(1)
- Filter拿到 Some(1) ,执行filter谓词 &1 >1 → false
- 不返回,loop继续,再次调用 self.iter.next()
- Map再次向上游Iter取下一个元素 &2 → map得到 2 返回Some(2)
- filter判断 &2>1 → true → return Some(2) 给外部消费者。
看清楚:filter内部不满足条件时,会再次调用上游next(),上游又完整走一遍map逻辑处理下一个元素。
元素是一个一个流动,不是批量。没有中间数组。
再看三层链式例子 map → filter → flat_map
rust
vec![vec![1,2],vec![3,4]]
.iter()
.map(|v| {println!("map {:?}",v); v})
.filter(|v| {println!("filter {:?}",v); v.len()>1})
.flat_map(|v| {println!("flat_map {:?}",v); v})
.for_each(|x| println!("消费 {}",x));
执行流:
- 消费调用flat_map.next()
- flat_map内部先向上游filter.next()
- filter向上游map.next()
- map向上原始迭代器拿 &vec1,2 → map执行 → filter执行 → 通过
- flat_map拿到 &vec1,2 ,把它转为子迭代器;逐个产出1,2
- 子迭代器耗尽之后,flat_map才会再次向上游filter.next()获取下一个外层元素。
输出顺序:
txt
map [1, 2]
filter [1, 2]
flat_map [1, 2]
消费 1
消费 2
map [3, 4]
filter [3, 4]
flat_map [3, 4]
消费 3
消费 4
外层元素处理完,flat_map把内层全部吐完,才回去拉下一个外层元素。
关键:flat_map行为特殊点
flat_map会把内层迭代器全部消耗完,才再次向上游请求下一个元素。其他适配器(map/filter)都是来一个处理一个。
两种模型对比表格
| 模式 | 行为 | rust迭代器链式 |
|---|---|---|
| 批量流水线(先全部A,再全部B) | A全部跑完存入中间集合,再交给B | ❌ 迭代器不是;只有手动collect才会这样 |
| 流式逐个元素流水线 | 元素1:A→B→C;元素2:A→B→C | ✅Rust迭代器链式真实行为 |
构造迭代器链的时候,方法调用顺序是 iter().map().filter() ,这是组装嵌套结构体;
执行next的时候调用链是: 消费者 → filter.next → map.next → source.next,调用栈是从下游往上游发起拉取。
👉 拉取模型(Pull-based):迭代器是拉取模式,下游主动向上游要数据;不是上游主动推送数据给下游。
这是理解Rust迭代器一切的根基。不是上游推给下游;下游需要数据,主动向上游拉。
常见迷惑考题
问题1:下面代码,map闭包执行几次?
rust
let v = vec![1,2,3,4];
let res = v.iter()
.map(|x| println!("run map {}", x))
.filter(|_| false)
.next();
filter条件永远false,只调用一次next()。
执行输出:run map 1
分析:
- filter.next() → 拉取map.next(),执行map得到元素1;filter判断false;loop继续,再次调用map.next()
- map.next() → 拉原始迭代器,元素2,map执行;filter false;loop继续
- map.next() → 元素3 map执行;filter false
- map.next() → 元素4 map执行;filter false
- map.next() → None,filter返回None结束。
map总共执行4次!虽然我们只调用 .next() 一次。因为filter内部loop不断向上游拉取元素。
这里非常多同学踩坑:只调用一次next,上游map却跑多次。根源就是filter内部循环向上游拉数据。
总结,两句话记牢
- 构造阶段: source.map().filter().flat_map() ,从左向右调用适配器方法,生成一层层嵌套迭代器结构体,不处理任何元素,惰性。
- 消费阶段(next/collect/for_each):下游主动向上游拉取元素;每拉取一个元素,从源头出发,依次流过整条链所有适配器;没有中间集合;filter这类适配器内部会循环向上游反复拉取,直到找到符合条件或者上游结束。