迭代器是 Rust 中处理数据的一种强大工具,就像流水线一样,可以让数据一步步经过不同的处理环节。 它有两个重要特点:一是“懒惰”——只有在真正需要结果时才开始工作;二是可以任意组合——我们可以把多个处理步骤串联起来。

这三个方法分别对应集合的不同借用方式,理解它们的区别是掌握迭代器的第一步:
iter():创建一个对集合元素的不可变引用迭代器,原集合保持不变,可以继续使用iter_mut():创建一个对集合元素的可变引用迭代器,可以修改原集合中的元素into_iter():消费集合并获得所有权,创建一个拥有元素的迭代器让我们通过一个例子来看看它们的区别。假设我们有一个数字向量,想要进行不同的操作:
|fn main() { let mut v = vec![1, 2, 3]; let sum_readonly: i32 = v.iter().copied().sum(); for x in v.iter_mut() { *x *= 2; } let collected: Vec<_> = v.into_iter().map(|x| x + 1).collect(); println!("sum={sum_readonly}, collected={:?}", collected); }
|sum=6, collected=[3, 5, 7]
在 Rust 的迭代器系统中,我们需要理解两个核心概念:适配器和消费器。它们就像工厂流水线中的不同环节。
适配器(Iterator Adaptors) 是数据的"变形师",它们接收一个迭代器,对其进行某种变换,然后返回一个新的迭代器。重要的是,适配器本身是懒惰的——它们不会立即执行,只是描述了要做什么:
map():对每个元素应用函数进行转换filter():根据条件筛选元素flat_map():先映射再展平,处理嵌套结构scan():带状态的映射,类似累积器take():只取前 n 个元素skip():跳过前 n 个元素inspect():不改变数据,但可以观察每个元素(常用于调试)消费器(Consumers) 则是真正的"执行者",它们会消费迭代器并产生最终结果。只有调用消费器,整个迭代器链才会开始工作:
collect():收集所有元素到集合中sum()/product():计算总和或乘积for_each():对每个元素执行副作用操作fold()/reduce():累积计算all()/any():检查是否所有/任意元素满足条件通过巧妙组合适配器和消费器,我们可以构建出表达力极强的数据处理流水线,代码既简洁又高效。
让我们通过一个例子来看看适配器和消费器的区别。假设我们有一个单词列表,想要进行不同的操作:
|fn main() { let words = ["rust", "is", "fast"]; let upper_joined = words .iter() .map(|w| w.to_uppercase()) .filter(|w| w.len() > 2)
|debug: RUST debug: FAST RUST,FAST
Rust 的迭代器系统有一个非常优雅的设计:通过 collect() 方法,我们可以将任何迭代器转换成各种标准集合类型。这背后的魔法来自于 FromIterator trait——所有标准集合都实现了这个 trait,使得它们能够从迭代器中构建自己的实例。
collect() 方法非常智能,它可以根据上下文自动推断目标类型。当 Rust 编译器无法确定具体类型时,我们可以使用类型注解来明确指定。最常见的方式是使用"涡轮鱼"语法(turbofish syntax)::<Type>,或者在变量声明时指定类型。
让我们通过一个例子来看看如何从迭代器构造集合。
|use std::collections::{HashMap, HashSet}; fn main() { let set: HashSet<_> = (0..5).map(|x| x * 2).collect(); let map: HashMap<_, _> =
在迭代器的世界中,有一类特殊的消费器被称为“短路消费器”,它们具有一个重要特性:当遇到特定条件时会立即停止迭代,而不是处理完所有元素。
常见的短路消费器包括:
find() 方法:寻找第一个满足条件的元素。一旦找到匹配项,它就会立即返回 Some(element),不会继续检查后续元素。如果遍历完所有元素都没有找到匹配项,则返回 None。any() 方法:检查是否有任意一个元素满足条件。只要找到一个满足条件的元素,就立即返回 true,无需检查剩余元素。只有当所有元素都不满足条件时才返回 false。all() 方法:检查是否所有元素都满足条件。一旦发现有元素不满足条件,就立即返回 false,不会继续检查后续元素。只有当所有元素都满足条件时才返回 true。position() 方法:寻找第一个满足条件的元素的位置索引。找到后立即返回 Some(index),否则返回 None。take_while() 方法:从迭代器开头取元素,直到遇到第一个不满足条件的元素为止。这是一个适配器方法,它会在条件不满足时停止生成新元素。这种短路行为在处理大型数据集或无限迭代器时特别有价值,因为它避免了不必要的计算开销,让我们的程序既快速又节省资源。
看一个简单的例子:
|fn main() { let idx = (0..).take(1000).position(|x| x % 37 == 0 && x > 0).unwrap(); println!("idx={idx}"); }
上面的例子中,我们创建了一个无限迭代器 (0..),然后使用 take(1000) 限制其长度为 1000,再使用 position 方法找到第一个满足条件的元素的位置。由于我们在条件中添加了 x > 0,所以第一个满足条件的元素是 37。
Iterator在 Rust 中,任何类型都可以通过实现 Iterator trait 来成为迭代器。这个 trait 的核心非常简单——只需要定义一个 next() 方法,该方法返回 Option<Self::Item>。
当有下一个元素时返回 Some(item),当迭代结束时返回 None。
Iterator trait 的定义大致如下:
|struct Stepper { current: i32, end: i32, step: i32 } impl Stepper { fn new(start: i32, end: i32, step: i32) -> Self { Self { current: start, end, step } } } impl Iterator for Stepper { type Item =
|[0, 6, 12]
scan 与状态机scan 方法是一个强大的适配器,它允许我们在迭代过程中维护和更新一个内部状态。与 map 不同的是,scan 不仅可以转换每个元素,还能在处理过程中累积信息,这使得它特别适合构建轻量级的状态机或实现需要记忆前面处理结果的算法。
scan 的工作原理是:它接受一个初始状态和一个闭包函数,闭包函数会接收到当前状态的可变引用和当前元素,然后返回一个 Option。如果返回 Some(value),则 value 会被添加到结果迭代器中,状态也会被更新;如果返回 None,则迭代提前结束。
这种设计让 scan 成为处理累积计算、滑动窗口、状态跟踪等场景的理想工具。我们可以把它想象成一个带记忆的 map 操作,每次处理都能"记住"之前的计算结果。
|fn main() { let prefix_sum: Vec<i32> = [1,2,3,4] .into_iter() .scan(0, |acc, x| { *acc += x; Some(*acc) }) .collect();
|[1, 3, 6, 10]
9. 三种迭代器入口练习
给定一个可变向量 vec![1, 2, 3],完成以下三个任务:
iter() 创建只读迭代器,计算所有元素的和(使用 copied() 将引用转换为值,然后使用 sum())。注意:使用 iter() 后,原向量 v 仍然可以继续使用。iter_mut() 创建可变迭代器,在循环中将每个元素翻倍(使用 *x *= 2)。注意:iter_mut() 允许修改原集合中的元素。into_iter() 消费向量,创建一个新向量,其中每个元素都减1(使用 map(|x| x - 1) 和 collect())。注意:使用 into_iter() 后,原向量 v 不能再使用。|fn main() { // A. 只读求和(保持 v 可继续使用) let mut v = vec![1, 2, 3]; let sum: i32 = v.iter() // iter()创建不可变引用迭代器 .copied() // 将&i32转换为i32 .sum(); // 求和 assert_eq!(sum, 6); println!(
10. 链式迭代器操作练习
使用链式迭代器操作,完成以下任务:
1..=20 开始filter(|x| x % 2 != 0))map(|x| x * x))skip(3))take(4))Vec<i32>[121, 169, 225, 289]提示:奇数序列的平方是 [1, 9, 25, 49, 81, 121, 169, 225, 289, ...],跳过前3个后取4个就是 [121, 169, 225, 289]。
|fn main() { let v: Vec<i32> = (1..=20) .filter(|x| x % 2 != 0) // 筛选奇数 .map(|x| x * x) // 取平方 .skip(3
11. 集合构造练习
完成以下两个任务:
["rust", "rust", "book", "fast"] 去重后收集为 HashSet。注意:数组需要先转换为迭代器(使用 .iter() 或 .into_iter()),然后使用 collect() 收集到 HashSet。HashMap<usize, usize>,其中键是索引,值是单词长度。使用 enumerate() 获取索引,使用 map() 将每个元素转换为 (索引, 长度) 元组,然后使用 collect() 收集。|use std::collections::{HashMap, HashSet}; fn main() { let words = ["rust", "rust", "book", "fast"]; // 任务A:去重后收集为HashSet let set: HashSet<_> = words.into_iter().collect(); assert!
12. 短路消费器练习
完成以下两个任务:
1.. 中寻找第一个同时被 7 和 11 整除的正数。使用 find() 方法,条件是两个数都能整除(x % 7 == 0 && x % 11 == 0)。注意:find() 是短路消费器,找到第一个满足条件的元素后就会停止。1..100 中是否存在平方为偶数的奇数。使用 any() 方法,条件是 x 为奇数(x % 2 != 0)且 x * x 为偶数((x * x) % 2 == 0)。注意:奇数的平方永远是奇数,所以结果应该是 false。|fn main() { // 任务A:寻找第一个同时被7和11整除的数 let n = (1..) .find(|x| x % 7 == 0 && x % 11 == 0) // 找到第一个满足条件的元素 .unwrap(); // 解包Option assert_eq!(n, 77); println!("n={}"
13. 自定义迭代器实现练习
实现一个 Fib 结构体,它实现 Iterator trait,用于生成斐波那契数列。
要求:
Fib 结构体包含两个字段:a: u64 和 b: u64,分别表示当前和前一个斐波那契数new() 方法创建初始状态:a = 0, b = 1next() 方法实现迭代逻辑:
a 值(包装在 Some 中)(a, b) = (b, a + b),即下一个斐波那契数是前两个数的和a 的值用于返回,然后更新状态在 main 函数中,使用 Fib::new().take(10).collect() 收集前10个斐波那契数,验证结果是 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]。
|struct Fib { a: u64, b: u64 } impl Fib { fn new() -> Self { Self { a: 0, b: 1 } } } impl Iterator for Fib { type Item = u64;
14. scan适配器练习
使用 scan() 方法计算数组 [2, 3, 5, 7, 11] 的前缀和,但有一个特殊要求:一旦前缀和超过 10 就停止产生新元素。
要求:
scan() 方法维护一个累加器(初始值为 0)None 提前结束迭代Some(累加值) 并更新累加器[2, 5, 10](2, 2+3=5, 5+5=10,下一个 10+7=17 超过10,停止)|fn main() { let data = [2, 3, 5, 7, 11]; let prefix: Vec<i32> = data .into_iter() .scan(0, |acc, x| { // scan维护累加器,初始值为0 *acc +=
15. fold累积操作练习
使用 fold() 方法统计字符串数组 ["rust", "fast", "rust", "safe", "fast", "rust"] 中每个单词的出现次数,将结果收集到 HashMap<&str, usize> 中。
要求:
fold() 方法,初始值为 HashMap::new()entry() API 或 get() + insert() 的方式"rust" 出现 3 次,"fast" 出现 2 次,"safe" 出现 1 次|use std::collections::HashMap; fn main() { let words = ["rust", "fast", "rust", "safe", "fast", "rust"]; let counts: HashMap<&str, usize> = words .into_iter()
|sum=6 v=[2, 4, 6] collected=[1, 3, 5]
说明:
iter() 创建不可变引用迭代器,不消费集合,可以继续使用原集合iter_mut() 创建可变引用迭代器,可以修改元素,但不消费集合into_iter() 消费集合,获得所有权,使用后原集合不能再使用copied() 将引用转换为值,用于 iter() 迭代器sum() 是消费器,计算所有元素的和|[121, 169, 225, 289]
说明:
filter() 用于筛选满足条件的元素map() 用于转换每个元素skip() 跳过前n个元素take() 只取前n个元素collect() 时,整个迭代器链才会执行|set={"rust", "book", "fast"} lengths={0: 4, 1: 4, 2: 4, 3: 4}
说明:
HashSet 自动去重,重复的元素只会保留一个enumerate() 为迭代器添加索引,返回 (usize, Item) 元组collect() 可以根据目标类型自动推断如何收集HashSet<_>)帮助编译器推断类型|n=77 exists=false
说明:
find() 是短路消费器,找到第一个满足条件的元素后立即返回 Some(element)any() 是短路消费器,只要找到一个满足条件的元素就返回 true|[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
说明:
Iterator trait 需要定义 type Item 和 next() 方法next() 返回 Option<Self::Item>,有下一个元素时返回 Some(item),迭代结束时返回 NoneF(n) = F(n-1) + F(n-2)take())组合使用|[2, 5, 10]
说明:
scan() 接受一个初始状态和一个闭包函数&mut State 和当前元素,返回 Option<Output>Some(value) 时,value 会被添加到结果迭代器中,状态也会被更新None 时,迭代会提前结束scan() 特别适合实现带状态的累积计算和提前终止的逻辑|{"rust": 3, "fast": 2, "safe": 1}
说明:
fold() 接受一个初始值和一个闭包函数HashMap)和当前元素,返回新的累积值entry() API 提供了优雅的方式来处理"存在则更新,不存在则插入"的逻辑or_insert(0) 如果键不存在则插入 0,然后返回值的可变引用fold() 是通用的累积操作,可以用于实现各种聚合功能,如求和、计数、分组等