Rust Iterator
什么是 Rust 迭代器?
Rust 的迭代器(Iterator)是一种强大的抽象,它允许你遍历集合元素而不需要关心底层数据结构的具体实现。迭代器模式是函数式编程的核心概念之一,Rust 在这方面做得特别出色。
迭代器的基本概念
在 Rust 中,任何实现了 Iterator trait 的类型都可以称为迭代器:
1 | |
核心方法 next() 返回 Option<Item>,当迭代完成时返回 None。
创建迭代器的方法
iter() - 不可变引用迭代
1 | |
iter_mut() - 可变引用迭代
1 | |
into_iter() - 获取所有权迭代
1 | |
常用迭代器方法详解
map() - 转换元素
map() 对迭代器中的每个元素应用一个闭包,将元素从一种类型转换为另一种类型。它是惰性适配器——调用 map() 本身不会执行任何计算,只有在消费迭代器时(如 collect()、for 循环)才会真正应用闭包。
类型签名: fn map<B, F>(self, f: F) -> Map<Self, F> where F: FnMut(Self::Item) -> B
要点:
- 闭包的参数类型取决于迭代器产生的元素类型(
iter()产生&T,into_iter()产生T) map()不会改变迭代器的长度——每个输入元素恰好产生一个输出元素- 常见用途:类型转换、字段提取、数值变换
1 | |
filter() - 过滤元素
filter() 保留满足条件的元素,丢弃不满足条件的元素。闭包返回 true 时保留元素,返回 false 时丢弃。同样是惰性操作——调用 filter() 不会遍历集合。
类型签名: fn filter<P>(self, predicate: P) -> Filter<Self, P> where P: FnMut(&Self::Item) -> bool
要点:
- 闭包接收的是元素的引用(
&Self::Item),而非元素本身——这是设计决定,避免消费元素 |&x|模式用于解构引用,让闭包体中直接使用值而非引用filter()不会改变元素本身,只决定保留或丢弃- 常见用途:条件筛选、空值过滤、范围限制
1 | |
filter_map() - 过滤并转换
filter_map() 是 filter() 和 map() 的组合:闭包返回 Some(value) 时保留并转换元素,返回 None 时丢弃元素。比先 filter() 再 map() 更简洁高效,因为只需一次遍历。
类型签名: fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> where F: FnMut(Self::Item) -> Option<B>
要点:
- 适用于“可能失败的转换”场景,如字符串解析、类型转换、查找操作
filter_map(|x| x)是过滤Option值的惯用写法(flatten()也可)- 比单独的
filter().map()避免了额外的迭代器适配器开销 - 可以改变元素类型,而
filter()只能保留原类型
1 | |
fold() - 累积计算
fold() 从一个初始值开始,依次对每个元素应用闭包,将结果累积到最终值。它是迭代器最通用的消费方法——sum()、product()、count() 等都可以用 fold() 实现。
类型签名: fn fold<B, F>(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B
要点:
- 第一个参数是初始累积值,决定最终结果的类型
- 闭包的第一个参数
acc是当前累积值,第二个参数是迭代元素 - 与
reduce()的区别:fold()需要显式提供初始值,可以改变类型;reduce()用第一个元素作初始值,类型不变 - 空迭代器对
fold()安全——直接返回初始值
1 | |
reduce() - 归约操作
reduce() 与 fold() 类似,但使用迭代器的第一个元素作为初始累积值,因此不需要显式提供初始值。返回 Option<T>——空迭代器返回 None。
类型签名: fn reduce<F>(self, f: F) -> Option<Self::Item> where F: FnMut(Self::Item, Self::Item) -> Self::Item
要点:
- 累积值的类型与元素类型相同(无法像
fold()那样改变类型) - 空迭代器返回
None,需要用unwrap()或模式匹配处理 - 适合同类型归约:求和、求最大值、字符串拼接等
- 如果需要改变累积值类型,请使用
fold()而非reduce()
1 | |
take() - 取前 n 个元素
take() 从迭代器开头取指定数量的元素,返回一个最多包含 n 个元素的新迭代器。对于有限迭代器,如果元素少于 n,则返回所有元素;对于无限迭代器,take() 限制为恰好 n 个。
类型签名: fn take(self, n: usize) -> Take<Self>
要点:
- 常用于配合无限迭代器生成有限序列,如
0..是无限的,加上.take(5)就是有限的[0,1,2,3,4] take(0)返回一个空迭代器- 与
skip()相对:take 取开头,skip 跳过开头 - 惰性操作:调用
take()不消费任何元素,只创建新迭代器
1 | |
skip() - 跳过前 n 个元素
skip() 跳过迭代器开头的 n 个元素,返回从第 n+1 个元素开始的迭代器。如果迭代器元素少于 n,则返回空迭代器。
类型签名: fn skip(self, n: usize) -> Skip<Self>
要点:
skip(0)返回一个等价的迭代器(不跳过任何元素)- 与
take()相对:take 取开头,skip 跳过开头 - 变体
skip_while()跳过满足条件的连续元素,一旦遇到不满足条件的元素就停止跳过 - 适用于分页、跳过表头、跳过前置条件等场景
1 | |
take_while() - 取满足条件的元素
take_while() 从开头开始取元素,只要闭包返回 true 就继续,一旦遇到返回 false 的元素就立即停止,不再处理后续元素。
类型签名: fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P> where P: FnMut(&Self::Item) -> bool
要点:
- 与
filter()的区别:filter 检查所有元素,take_while 只检查连续元素 - 一旦遇到不满足条件的元素,迭代立即终止——后续元素不会被处理
- 适用于“取到某个临界点为止”的场景,如前缀序列、连续满足条件的部分
- 变体
take(n)是take_while的特例:按索引而非条件截断
1 | |
chain() - 连接多个迭代器
chain() 将两个迭代器首尾相连,返回一个先遍历第一个迭代器、再遍历第二个迭代器的新迭代器。它实现了迭代器的拼接操作。
类型签名: fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where U: IntoIterator<Item = Self::Item>
要点:
- 接收的是
IntoIterator,所以可以传入Vec、数组、Range等,自动调用into_iter() - 元素类型必须一致——chain 不会做类型转换
- 可以链式调用多次:
.chain(a).chain(b).chain(c) - 与
chain()类似的扁平化操作还有flat_map(),但 flat_map 会把闭包返回的每个迭代器展开
1 | |
zip() - 组合迭代器
zip() 将两个迭代器的元素按索引配对,生成一个元组迭代器。当两个迭代器长度不同时,以较短的为准,长的那部分多余元素被丢弃。
类型签名: fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where U: IntoIterator
要点:
- 返回类型是
(Self::Item, U::Item)元组的迭代器 - 长度不一致时不会报错,只会丢失较长迭代器的多余元素——需要自行保证长度匹配
- 常用于“并行遍历”多个集合:配对姓名和年龄、键和值、坐标 x 和 y 等
- 如果需要同时遍历多个,用
multiunzip()扩展 trait(需要itertoolscrate)
1 | |
enumerate() - 添加索引
enumerate() 为迭代器的每个元素添加一个从 0 开始的序号,返回 (索引, 元素) 元组的迭代器。这是 Rust 中最常用的获取索引的方式。
类型签名: fn enumerate(self) -> Enumerate<Self>
要点:
- 索引从 0 开始,依次递增
- 返回的元组类型是
(usize, Self::Item)——索引是usize,元素保持原类型 - 与
zip()结合可以与其他序列配对,如enumerate().zip(ids.iter()) - 常用于需要下标的场景:打印行号、基于索引查找、条件判断时需要序号
1 | |
rev() - 反转迭代器
rev() 返回一个反向遍历的迭代器。注意:只有实现了 DoubleEndedIterator trait 的迭代器才能反转——这要求迭代器可以从两端消费。
类型签名: fn rev(self) -> Rev<Self> where Self: DoubleEndedIterator
要点:
- 并非所有迭代器都可反转:
Range<i32>可以,但HashSet的迭代器不行 - 调用
rev()时如果迭代器不支持,会导致编译错误 - 反转后再反转会回到原始顺序
- 与
enumerate()结合可实现反向索引:(0..n).rev()产生[n-1, n-2, ..., 0]
1 | |
flat_map() - 扁平化映射
flat_map() 对每个元素应用闭包,将返回的多个迭代器合并成一个扁平的迭代器。本质上是 map().flatten() 的组合,但更高效(只遍历一次)。
类型签名: fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> where F: FnMut(Self::Item) -> U, U: IntoIterator
要点:
- 闭包返回的是一个
IntoIterator,可以是Vec、切片、Range、字符串等 flatten()可以单独使用,作用是把嵌套的迭代器展平- 常见用途:分割字符串、展开嵌套结构、生成多个结果
- 与
map()的区别:map 的闭包返回单个值,flat_map 的闭包返回多个值(迭代器)
1 | |
cloned() - 克隆元素
cloned() 将迭代器的元素从引用类型转换为值类型,通过调用 clone() 实现。对于实现了 Copy trait 的类型,克隆是零成本操作(只是位复制)。
类型签名: fn cloned<'a, T>(self) -> Cloned<Self> where T: 'a + Clone, Self: Iterator<Item = &'a T>
要点:
iter()返回&T,cloned()将其转换为T(调用 clone)- 对于
Copy类型(i32、char、bool 等),cloned()等价于copied(),没有任何运行时开销 - 对于非
Copy类型(如String、Vec<T>),cloned()会进行深拷贝——注意性能 - 另一种选择是
copied(),适用于Copy类型且零拷贝
1 | |
collect() - 收集为集合
collect() 是迭代器最重要的消费方法之一,将迭代器的所有元素收集到一个集合类型中。它依赖 FromIterator trait 来实现各种集合类型的构造。
类型签名: fn collect<B>(self) -> B where B: FromIterator<Self::Item>
要点:
- 必须显式指定目标类型,因为类型推断可能不明确:
let v: Vec<i32> = iter.collect() - 支持的集合类型:
Vec<T>、HashSet<T>、HashMap<K, V>、BTreeSet<T>、String(字符集合)、Result<T, E>等 - 对于需要键值对的集合(如
HashMap),迭代器需要产生(K, V)元组 collect()是急切操作——会立即遍历并消费整个迭代器
1 | |
sum() 和 product() - 求和与求积
sum() 计算所有元素的总和,product() 计算所有元素的乘积。它们是 fold() 的特化实现,专门针对数值运算进行了优化。
类型签名:
fn sum<S>(self) -> S where S: Sum<Self::Item>fn product<P>(self) -> P where P: Product<Self::Item>
要点:
- 必须显式指定返回类型,因为编译器无法推断:
let sum: i32 = vec.iter().sum() - 默认实现了
Sum和Producttrait 的类型包括:所有数字类型(i32、f64、BigInt 等) - 可以对实现了这些 trait 的自定义类型使用
sum()等价于fold(0, |acc, x| acc + x),product()等价于fold(1, |acc, x| acc * x)
1 | |
max() 和 min() - 最大最小值
max() 和 min() 返回迭代器中的最大/最小元素。它们要求元素类型实现 PartialOrd trait,以便进行大小比较。
类型签名:
fn max(self) -> Option<Self::Item> where Self::Item: PartialOrdfn min(self) -> Option<Self::Item> where Self::Item: PartialOrd
要点:
- 返回
Option<T>—— 空迭代器返回None,非空返回Some(value) - 使用
PartialOrd而非Ord,因为某些类型(如浮点数f64)无法全序比较 max_by_key()和min_by_key()允许自定义比较键:按字符串长度找最长、按年龄找人最年长等- 对于自定义比较逻辑,
max_by()接受一个比较函数,可实现任意比较规则
1 | |
any() 和 all() - 检查条件
any() 检查是否存在满足条件的元素(短路求值),all() 检查是否所有元素都满足条件。它们是短路操作:一旦结果确定就停止迭代。
类型签名:
fn any<P>(self, predicate: P) -> bool where P: FnMut(&Self::Item) -> boolfn all<P>(self, predicate: P) -> bool where P: FnMut(&Self::Item) -> bool
要点:
any()找到第一个满足条件的就返回true,全部不满足才返回falseall()遇到第一个不满足条件的就返回false,全部满足才返回true- 短路行为带来性能优势:对于大数据集,不必遍历全部元素
- 闭包返回类型是
bool,不是Option或Result
1 | |
find() - 查找元素
find() 从迭代器开头开始查找第一个满足条件的元素,返回 Option<&T>。找到后立即停止迭代(短路行为)。
类型签名: fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where P: FnMut(&Self::Item) -> bool
要点:
- 返回的是元素的引用(
Option<&T>),而非元素本身 - 闭包参数是
&Self::Item,所以iter()产生的&T在闭包中变成&&T,需要|&&x|解构 - 只返回第一个匹配元素,如需所有匹配请用
filter().collect() - 变体
find_map()结合了find和map:返回第一个Some(value),同时转换类型
1 | |
position() - 查找位置
position() 查找第一个满足条件的元素的索引位置,返回 Option<usize>。与 find() 类似但返回索引而非元素本身。
类型签名: fn position<P>(&mut self, predicate: P) -> Option<usize> where P: FnMut(Self::Item) -> bool
要点:
- 返回的是索引(
usize),不是元素引用 - 闭包参数是
Self::Item(而非&Self::Item),所以iter()产生的&T在闭包中是&T,用|&x|解构 - 只返回第一个匹配的索引,重复元素不会返回多个
- 与
enumerate().find()等价,但position()更简洁高效
1 | |
fold_while() - 条件累积
fold_while() 是 itertools crate 提供的扩展方法,与 fold() 类似但支持提前终止。闭包返回 FoldWhile::Continue(value) 继续迭代,返回 FoldWhile::Done(value) 立即停止。
类型签名: fn fold_while<T, F>(self, init: T, f: F) -> FoldWhile<T> where F: FnMut(T, Self::Item) -> FoldWhile<T>
要点:
- 来自
itertoolscrate,需要use itertools::FoldWhile::{Continue, Done}; fold()无法提前终止——即使累积值已满足条件也会遍历所有元素fold_while()在满足条件时立即停止,避免不必要的计算into_inner()从FoldWhile中提取最终累积值,无论是因为Done还是迭代结束
1 | |
scan() - 带状态的转换
scan() 与 fold() 类似,但闭包可以输出中间结果而非只返回最终值。它维护一个可变状态,每次迭代产生一个 Option 输出——Some(value) 输出值,None 停止迭代。
类型签名: fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F> where F: FnMut(&mut St, Self::Item) -> Option<B>
要点:
- 状态是
&mut可变引用,闭包可以修改它 - 返回
Option<B>:Some表示输出一个值,None表示停止迭代 - 与
fold()的区别:fold 只返回最终值,scan 输出每一步的中间结果 - 典型用途:前缀和、运行最大值、状态机、斐波那契数列等需要跟踪状态的序列生成
1 | |
cycle() - 循环迭代器
cycle() 将一个有限迭代器变为无限循环迭代器——遍历完所有元素后从头开始,永不停歇。通常与 take() 配合使用来限制重复次数。
类型签名: fn cycle(self) -> Cycle<Self> where Self: Clone
要点:
- 要求迭代器实现
Clone——因为需要保存初始状态以便重新开始 - 空迭代器调用
cycle()仍然是空的(无法循环空序列) - 常见模式:
iter.cycle().take(n)实现重复 n 次的效果 - 适用于轮询调度、循环缓冲、重复模式等场景
1 | |
chunks() 和 windows() - 窗口操作
chunks() 将切片分割为不重叠的固定大小块,windows() 生成滑动窗口(相邻窗口有重叠)。它们是切片方法而非迭代器方法,但返回迭代器。
类型签名:
fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>fn windows(&self, size: usize) -> Windows<'_, T>
要点:
chunks()不重叠:[1,2,3,4,5].chunks(2)→[[1,2], [3,4], [5]],最后一块可能不足指定大小windows()有重叠:[1,2,3,4].windows(2)→[[1,2], [2,3], [3,4]],每个窗口恰好指定大小windows()产生的窗口数量 =len() - size + 1,如果 size > len 则返回空迭代器- 典型用途:分批处理(chunks)、移动平均/滑动计算(windows)
1 | |
step_by() - 步长迭代
step_by() 从迭代器的第一个元素开始,每隔指定步长取一个元素。步长为 1 时等价于原迭代器,步长为 2 时每隔一个取一个。
类型签名: fn step_by(self, step: usize) -> StepBy<Self>
要点:
- 始终包含第一个元素,然后按步长跳过
step_by(1)等价于原迭代器,step_by(2)取奇数位元素- 步长为 0 会 panic(无意义操作)
- 与
skip()结合可实现“从第 n 个开始每隔 m 个取一个”的模式 - 适用于采样、隔行处理、等间隔选取等场景
1 | |
高级迭代器技巧
自定义迭代器
1 | |
迭代器链
1 | |
性能优化:使用迭代器而非循环
1 | |
总结
Rust 的迭代器系统是其最强大的特性之一:
- 零成本抽象:迭代器在编译时被优化为高效的代码
- 类型安全:编译器确保所有操作都是类型安全的
- 惰性求值:大多数操作是惰性的,只在需要时执行
- 组合性:可以轻松组合多个操作
- 可读性:函数式风格使代码更清晰
掌握迭代器可以让你写出更简洁、更高效、更安全的 Rust 代码。通过组合各种迭代器方法,你可以避免手动编写循环,减少错误,并让代码更具表达力。


