Rust 迭代器多级链式调用执行顺序笔记

Rust 迭代器多级链式调用执行顺序笔记

Rust迭代器链式调用执行顺序核心真相

不是先把第一级全部跑完生成全部中间数据,再跑下一级;而是逐个元素,一个元素流经整条链的所有适配器。惰性求值。

核心结论

  1. 构造阶段source.map().filter() 只是组装嵌套迭代器结构体,不会处理任何元素,惰性。
  2. 执行阶段 :下游主动向上游拉取数据(Pull‑based 拉模式);单个元素依次流经整条适配器链,不是先完整跑完一级再跑下一级
  3. 不会自动生成中间容器;只有手动 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() 的完整执行流程:

  1. Filter.next() 进入loop
  2. 调用 self.iter.next() → 也就是 Map.next()
  3. Map.next() 调用 self.iter.next() → Iter.next() ,得到 Some(&1)
  4. Map执行map闭包,得到 1 返回 Some(1)
  5. Filter拿到 Some(1) ,执行filter谓词 &1 >1 → false
  6. 不返回,loop继续,再次调用 self.iter.next()
  7. Map再次向上游Iter取下一个元素 &2 → map得到 2 返回Some(2)
  8. 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));

执行流:

  1. 消费调用flat_map.next()
  2. flat_map内部先向上游filter.next()
  3. filter向上游map.next()
  4. map向上原始迭代器拿 &vec1,2 → map执行 → filter执行 → 通过
  5. flat_map拿到 &vec1,2 ,把它转为子迭代器;逐个产出1,2
  6. 子迭代器耗尽之后,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

分析:

  1. filter.next() → 拉取map.next(),执行map得到元素1;filter判断false;loop继续,再次调用map.next()
  2. map.next() → 拉原始迭代器,元素2,map执行;filter false;loop继续
  3. map.next() → 元素3 map执行;filter false
  4. map.next() → 元素4 map执行;filter false
  5. map.next() → None,filter返回None结束。

map总共执行4次!虽然我们只调用 .next() 一次。因为filter内部loop不断向上游拉取元素。

这里非常多同学踩坑:只调用一次next,上游map却跑多次。根源就是filter内部循环向上游拉数据。

总结,两句话记牢

  1. 构造阶段: source.map().filter().flat_map() ,从左向右调用适配器方法,生成一层层嵌套迭代器结构体,不处理任何元素,惰性。
  2. 消费阶段(next/collect/for_each):下游主动向上游拉取元素;每拉取一个元素,从源头出发,依次流过整条链所有适配器;没有中间集合;filter这类适配器内部会循环向上游反复拉取,直到找到符合条件或者上游结束。
相关推荐
kyrie_sakura1 小时前
python学习笔记14 -- FastAPI
笔记·python·学习·fastapi
雪落漂泊1 小时前
C++11(上)
开发语言·c++
Kapaseker1 小时前
以为 Rust 快了 50000 倍,结果编译器根本没算
rust
春涧草茶2 小时前
慢就是快12-2三种访问权限|getter与setter|魔法方法
开发语言·python
传奇开心果编程2 小时前
【Xilem 0.4 基础语法学与练】第11课(深入篇):为什么其他 Rust 框架不是一切皆设计图
学习·rust·前端框架
無限進步D2 小时前
Java Web 前端 简介
java·开发语言·前端·css·html·css3·web
qq_401700412 小时前
Qt QUrl 详解与代码示例
开发语言·数据库·qt
小灰灰搞电子3 小时前
Rust Attribute(属性标记)完整整理
开发语言·后端·rust
tedcloud1233 小时前
God‘s Eye View 怎么搭建?在云服务器上部署一个实时 3D 地球可视化平台
linux·服务器·开发语言·后端·rust