直接回答:迭代器适配器(map、filter、take 等)返回的是记录转换逻辑的结构体,不立刻执行;只有遇到消费器(collect、sum、for 循环)才驱动整条链。编译器把整条链单态化展开成手写循环等价的机器码,没有堆分配、没有动态分发——这就是零成本:你不用的不付费,你用的不比手写慢。基准上迭代器链常优于显式索引循环,因为省略了边界检查。
展开解析:惰性带来几个实践要点:第一,iter.map(f) 单独一行是没有效果的语句(编译器警告 must_use),必须有消费器;第二,消费时才逐个求值,所以 filter 后接 next 只遍历到第一个命中,不会扫完全表;第三,collect 需要类型标注推断目标集合,collect::<Vec<_>>();第四,长链中间若需要多次遍历得先 collect 落盘,否则每条链都重新执行。性能上理解单态化:每个闭包和适配器组合生成专属类型,优化器能看到全部代码做内联——这与 Java Stream 装箱加 lambda 动态分发形成对比。需要运行时选择不同迭代逻辑时用 Box<dyn Iterator> 退回动态分发。
追问方向:for 循环与迭代器是什么关系(IntoIterator)?为什么链式迭代能消除边界检查?rayon 的并行迭代器如何切换?
(约 450 字)