Rust: Rust 迭代器(Iterators):惰性数据处理管道
最后更新:2026-08-26
迭代器(Iterator)是 Rust 中"惰性数据处理管道"——它不立即计算结果,而是逐个产出元素,让你用链式调用的方式声明式地处理数据序列。
迭代器就像一条工厂流水线:数据从一端进入,经过一道道工序(过滤、转换、截取、汇总),最终从另一端产出成品。每道工序只做一件事,但组合起来能完成复杂的处理任务。
1. 流水线工厂的故事
(1) 痛苦:用循环处理数据又臭又长
小明 (Xiao Ming) 是 Rust 工厂的流水线主管。他需要处理一批零件数据:
- 筛选出所有合格品(偶数编号)
- 给每个零件打上两倍标记
- 只取前 5 个
- 统计总和
他用传统的 for 循环来写:
fn main() {
let parts = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut result = vec![];
let mut count = 0;
for &part in &parts {
if part % 2 == 0 { // 步骤1: 筛选
let doubled = part * 2; // 步骤2: 转换
result.push(doubled);
count += 1;
if count == 5 { // 步骤3: 截取
break;
}
}
}
let sum: i32 = result.iter().sum();
println!("Result: {:?}, Sum: {}", result, sum);
}
代码虽然能运行,但逻辑分散在各处。如果需求变更为"再跳过前2个"或"再取偶数个",就要重写整个循环。
(2) 迭代器管道的方案
用迭代器链式调用重写上面逻辑:
fn main() {
let parts = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let sum: i32 = parts.iter()
.filter(|&&n| n % 2 == 0) // 步骤1: 筛选偶数
.map(|&n| n * 2) // 步骤2: 乘以2
.take(5) // 步骤3: 取前5个
.sum(); // 步骤4: 求和
println!("Sum: {}", sum);
}
代码从"你怎么做"(how)变成了"你要什么"(what)。每一行都是独立工序,可以随时插入、删除、调换顺序——就像在流水线上调整工位。
2. 概念图解
以下 Mermaid 图展示 Iterator trait 方法链从 iter() 到 collect() 的完整惰性处理流程:
graph LR
A["原始数据<br/>1..=20"] --> B["iter()<br/>创建迭代器"]
B --> C["filter(|n| n%2==0)<br/>筛选偶数"]
C --> D["map(|n| n*3)<br/>每个元素 ×3"]
D --> E["skip(2)<br/>跳过前2个"]
E --> F["take(5)<br/>取前5个"]
F --> G["collect()<br/>消费器: 触发求值"]
H["惰性求值: 仅构建管道<br/>不立即计算"] -.-> C
H -.-> D
H -.-> E
H -.-> F
I["消费器驱动执行<br/>产出最终结果"] -.-> G
G --> J["结果<br/>[18, 24, 30, 36, 42]"]
3. 你将学到
- Iterator trait 与
next方法:迭代器的核心契约,理解next()如何逐个产出元素 - 迭代器适配器:
map/filter/take/skip/chain等转换方法 - 消费器:
collect/sum/count/fold等驱动迭代器执行的方法 - 惰性求值:适配器不会立即执行,直到消费器被调用
- 自定义迭代器:为自己的类型实现
Iterator trait - 链式调用实战:组合适配器 + 消费器完成数据处理任务
4. 核心概念
graph TB
A[迭代器 Iterator] --> B[迭代器适配器<br>Adapter]
A --> C[消费器 Consumer]
B --> D["map(|x| x+1) 转换"]
B --> E["filter(|x| x>0) 过滤"]
B --> F["take(n) 取前n个"]
B --> G["skip(n) 跳过n个"]
B --> H["chain(other) 拼接"]
B --> I["enumerate() 加索引"]
C --> J["collect() 收集到集合"]
C --> K["sum() 求和"]
C --> L["count() 计数"]
C --> M["fold(init, fn) 折叠"]
C --> N["for x in iter 循环"]
B -.->|"❌ 惰性<br>不调用就不执行"| C
C --> O["触发求值"]
(1) 迭代器适配器 vs 消费器
| 特性 | 迭代器适配器 (Adapter) | 消费器 (Consumer) |
|---|---|---|
| 作用 | 转换迭代器(从一个迭代器变到另一个) | 驱动迭代器并产生最终结果 |
| 返回值 | 实现 Iterator 的新类型 |
具体值(如 Vec<T>、i32、usize) |
| 惰性 | 惰性(不立即执行) | 贪婪(立即执行) |
| 典型方法 | map, filter, take, skip, chain |
collect, sum, count, fold, for_each |
| 链式位置 | 中间步骤 | 末尾步骤 |
| 示例 | `.map( | x |
(2) 常用迭代器适配器速查
| 适配器 | 作用 | 示例 | 结果 |
|---|---|---|---|
map(f) |
对每个元素应用 f 转换 | [1,2,3].iter().map(|x| x*2) |
2, 4, 6 |
filter(p) |
保留满足条件 p 的元素 | [1..=5].filter(|x| x%2==0) |
2, 4 |
take(n) |
只取前 n 个元素 | [1..].take(3) |
1, 2, 3 |
skip(n) |
跳过前 n 个元素 | [1..=5].skip(2) |
3, 4, 5 |
chain(it) |
拼接另一个迭代器 | [1,2].iter().chain([3,4].iter()) |
1, 2, 3, 4 |
enumerate() |
给每个元素带上索引 (i, val) |
['a','b'].iter().enumerate() |
(0,'a'), (1,'b') |
zip(it) |
将两个迭代器一一配对 | [1,2].iter().zip(['a','b'].iter()) |
(1,'a'), (2,'b') |
rev() |
反转迭代器 | [1..=3].rev() |
3, 2, 1 |
(3) 常用消费器方法速查
| 消费器 | 作用 | 示例 | 返回类型 | 是否贪婪 |
|---|---|---|---|---|
collect() |
收集为集合 | iter.collect::<Vec<_>>() |
B: FromIterator |
是 |
sum() |
求和 | iter.sum::<i32>() |
S: Sum |
是 |
count() |
计数 | iter.count() |
usize |
是 |
fold(init, f) |
累积计算 | iter.fold(0, |acc, x| acc + x) |
初始值类型 | 是 |
reduce(f) |
无初始值折叠 | iter.reduce(|a, b| a + b) |
Option<Item> |
是 |
for_each(f) |
逐个执行(不返回值) | iter.for_each(|x| println!(x)) |
() |
是 |
any(p) |
是否存在满足条件 | iter.any(|x| x > 0) |
bool |
是 |
all(p) |
是否全部满足条件 | iter.all(|x| x > 0) |
bool |
是 |
find(p) |
找第一个满足条件的 | iter.find(|x| *x > 3) |
Option<Item> |
是 |
max() / min() |
最大/最小值 | iter.max() |
Option<Item> |
是 |
5. 示例
▶ 示例 1:Iterator trait 与 next() 方法——理解迭代器的本质(难度 ⭐)
// ============================================
// 手动调用 next() 理解迭代器的运作原理
// ============================================
fn main() {
let numbers = vec![10, 20, 30, 40, 50];
// iter() 返回一个迭代器,不会消费向量
let mut iter = numbers.iter();
// next() 每次返回 Option<&T>
// Some(&value) 表示还有下一个元素
// None 表示迭代结束
println!("{:?}", iter.next()); // Some(10)
println!("{:?}", iter.next()); // Some(20)
println!("{:?}", iter.next()); // Some(30)
println!("{:?}", iter.next()); // Some(40)
println!("{:?}", iter.next()); // Some(50)
println!("{:?}", iter.next()); // None
println!("{:?}", iter.next()); // None (再次调用仍返回 None)
// for 循环是 next() 的语法糖
let mut count = 0;
let iter2 = numbers.iter();
for val in iter2 {
println!("for loop #{}: {}", count, val);
count += 1;
}
}
输出:
Some(10)
Some(20)
Some(30)
Some(40)
Some(50)
None
None
for loop #0: 10
for loop #1: 20
for loop #2: 30
for loop #3: 40
for loop #4: 50
迭代器的核心契约就是
next()方法:每次调用返回Some(元素),耗尽后返回None。for循环就是不断调用next()直到遇到None的语法糖。理解这一点,就理解了一切迭代器的基础。
▶ 示例 2:迭代器适配器链式调用——map / filter / take / skip(难度 ⭐⭐)
// ============================================
// 组合多个适配器构建数据处理管道
// ============================================
fn main() {
// 原始数据:1 到 20
let data = 1..=20;
// 管道:筛选偶数 → 乘以3 → 跳过前2个 → 取前5个
let result: Vec<i32> = data
.filter(|&n| n % 2 == 0) // [2,4,6,8,10,12,14,16,18,20]
.map(|n| n * 3) // [6,12,18,24,30,36,42,48,54,60]
.skip(2) // [18,24,30,36,42]
.take(5) // [18,24,30,36,42]
.collect(); // 触发求值,收集到 Vec
println!("Pipeline result: {:?}", result);
// 另一个管道:用 chain 拼接两个切片
let first = vec!["A", "B", "C"];
let second = vec!["X", "Y", "Z"];
let combined: Vec<&str> = first.iter()
.chain(second.iter())
.copied()
.collect();
println!("Chained: {:?}", combined);
// 用 enumerate 给元素加索引
let fruits = vec!["apple", "banana", "cherry"];
let indexed: Vec<(usize, &str)> = fruits.iter()
.enumerate()
.map(|(i, &name)| (i + 1, name))
.collect();
println!("Indexed: {:?}", indexed);
}
输出:
Pipeline result: [18, 24, 30, 36, 42]
Chained: ["A", "B", "C", "X", "Y", "Z"]
Indexed: [(1, "apple"), (2, "banana"), (3, "cherry")]
适配器链就像流水线上的工位排列:每个适配器只做一件事,数据依次流过每个工位。
collect()是管道末端的需求信号——没有它,管道中的工人就不会开始工作(惰性求值)。
▶ 示例 3:自定义迭代器——为你的类型实现 Iterator trait(难度 ⭐⭐)
// ============================================
// 自定义 Fibonacci 迭代器
// 实现 Iterator trait 让任何类型可以迭代
// ============================================
// Fibonacci 序列生成器
struct Fibonacci {
current: u64,
next: u64,
max: u64,
}
impl Fibonacci {
fn new(max: u64) -> Self {
Fibonacci {
current: 0,
next: 1,
max,
}
}
}
// 实现 Iterator trait 是自定义迭代器的核心
impl Iterator for Fibonacci {
// Item 是迭代器产出的元素类型
type Item = u64;
// next() 返回 Option<Self::Item>
// Some(value) 表示还有下一个元素
// None 表示迭代结束
fn next(&mut self) -> Option<Self::Item> {
if self.current > self.max {
return None;
}
let result = self.current;
// 更新到下一个 Fibonacci 数
let new_next = self.current + self.next;
self.current = self.next;
self.next = new_next;
Some(result)
}
}
fn main() {
println!("Fibonacci up to 50:");
let fib = Fibonacci::new(50);
// Fibonacci 现在可以用在 for 循环中
for (i, n) in fib.enumerate() {
println!(" fib({}) = {}", i, n);
}
// 也可以和适配器链配合使用
println!("\nEven Fibonacci numbers up to 100:");
let even_fibs: Vec<u64> = Fibonacci::new(100)
.filter(|&n| n % 2 == 0)
.collect();
println!("{:?}", even_fibs);
}
输出:
Fibonacci up to 50:
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 34
Even Fibonacci numbers up to 100:
[0, 2, 8, 34]
实现
Iterator trait只需要做一件事:定义type Item(元素类型)和fn next()(产出规则)。一旦实现,你的类型就自动获得了所有适配器方法(map、filter、take等)——这就是"鸭子类型"在 Rust 中的体现:实现了next(),就像迭代器一样用。
▶ 示例 4:消费器实战——fold / sum / count / collect(难度 ⭐⭐⭐)
// ============================================
// 消费器:驱动迭代器执行并产生最终结果
// ============================================
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// sum() — 求和
let total: i32 = numbers.iter().sum();
println!("Sum: {}", total);
// count() — 计数
let cnt = numbers.iter().count();
println!("Count: {}", cnt);
// fold() — 通用折叠操作(初始值 + 累加器闭包)
// 这里计算阶乘 10!
let factorial: u64 = (1..=10u64).fold(1, |acc, x| acc * x);
println!("10! = {}", factorial);
// fold() — 手动实现 sum 和 count
let sum_via_fold: i32 = numbers.iter().fold(0, |acc, &x| acc + x);
let count_via_fold: usize = numbers.iter().fold(0, |acc, _| acc + 1);
println!("Sum (via fold): {}", sum_via_fold);
println!("Count (via fold): {}", count_via_fold);
// collect() — 收集到不同类型的集合
let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
println!("Doubled: {:?}", doubled);
let even_set: std::collections::HashSet<i32> = numbers.iter()
.filter(|&&x| x % 2 == 0)
.copied()
.collect();
println!("Even set: {:?}", even_set);
// 综合:找出前 5 个偶数的平方和
let complex_result: i32 = (1..=100)
.filter(|&n| n % 2 == 0)
.map(|n| n * n)
.take(5)
.fold(0, |acc, n| acc + n);
println!("Sum of first 5 even squares: {}", complex_result);
}
输出:
Sum: 55
Count: 10
10! = 3628800
Sum (via fold): 55
Count (via fold): 10
Doubled: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Even set: {2, 4, 6, 8, 10}
Sum of first 5 even squares: 220
消费器是管道末端的关键:
sum()、count()、fold()直接算出数值结果;collect()收集到集合中。fold()是最通用的消费器——sum()和count()本质上都是fold()的特化形式。一个管道必须有消费器才会真正执行,否则一切都是空谈。
▶ 示例 5:综合练习——自定义迭代器实现 FizzBuzz 生成器(难度 ⭐⭐⭐)
// ============================================
// 综合示例:自定义迭代器 + 适配器链
// ============================================
struct FizzBuzz {
current: u32,
limit: u32,
}
impl FizzBuzz {
fn new(limit: u32) -> Self {
FizzBuzz { current: 0, limit }
}
}
impl Iterator for FizzBuzz {
type Item = String;
fn next(&mut self) -> Option<String> {
self.current += 1;
if self.current > self.limit {
return None;
}
let n = self.current;
let result = match (n % 3, n % 5) {
(0, 0) => "FizzBuzz".to_string(),
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
_ => n.to_string(),
};
Some(result)
}
}
struct Fibonacci {
curr: u64,
next: u64,
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let result = self.curr;
self.curr = self.next;
self.next = result + self.next;
Some(result)
}
}
fn main() {
println!("=== FizzBuzz (1-20) ===");
for item in FizzBuzz::new(20) {
print!("{} ", item);
}
println!();
let fizz_count = FizzBuzz::new(100)
.filter(|s| s.starts_with("Fizz"))
.count();
println!("1-100 中 Fizz 出现次数: {}", fizz_count);
println!("\n=== Fibonacci 前 15 项 ===");
let fib = Fibonacci { curr: 0, next: 1 };
for val in fib.take(15) {
print!("{} ", val);
}
println!();
let fib_sum: u64 = Fibonacci { curr: 1, next: 1 }
.take_while(|&x| x < 1_000_000)
.filter(|&x| x % 2 == 0)
.sum();
println!("Fibonacci < 1M 的偶数和: {}", fib_sum);
}
输出:
=== FizzBuzz (1-20) ===
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz
1-100 中 Fizz 出现次数: 27
=== Fibonacci 前 15 项 ===
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Fibonacci < 1M 的偶数和: 1089154
自定义迭代器只需实现
Iteratortrait 的next()方法。FizzBuzz生成器可链式调用filter/count;Fibonacci无限迭代器用take/take_while限制输出,再用filter/sum聚合。
❓ 常见问题
iter()、into_iter() 和 iter_mut() 有什么区别?collect() 怎么知道要收集到什么类型?type Item 是什么?Item 是关联类型,指定 next() 返回的 Some 中的类型。fold() 和 reduce() 有什么区别?fold() 需要初始值,reduce() 使用第一个元素作为初始值。📖 小节
- Iterator trait 是 Rust 迭代器体系的基础,只需要实现
next()方法,返回Option<Self::Item> - 迭代器适配器(
map、filter、take、skip、chain)是惰性的——它们只记录操作,不执行计算 - 消费器(
collect、sum、count、fold)是管道的终点——调用它们才会触发实际计算 - 链式调用让数据处理代码从"你怎么做"(命令式)变为"你要什么"(声明式),更清晰、更可组合
- 自定义迭代器只需实现
Iterator trait,一旦实现就自动获得所有适配器方法 - 惰性求值是迭代器设计的核心理念——零开销抽象,只在需要时计算,避免不必要的中间分配
📝 作业
-
难度 ⭐:用迭代器重写下面这段代码。将
[1, 2, 3, 4, 5, 6, 7, 8]中所有奇数取出,乘以 10,收集到 Vec 中并打印。RUST// 用迭代器链替换这个循环 let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8]; let mut result = vec![]; for &n in &numbers { if n % 2 == 1 { result.push(n * 10); } } println!("{:?}", result); -
难度 ⭐⭐:为
struct StepRange { start: i32, end: i32, step: i32 }实现Iteratortrait,让它能像for n in StepRange::new(0, 10, 2)这样使用,输出 0, 2, 4, 6, 8, 10。然后用map将每个值平方,用collect收集到 Vec 中。 -
难度 ⭐⭐⭐:写一个函数
fn word_count(text: &str) -> std::collections::HashMap<String, usize>,使用迭代器方法统计一段文本中每个单词出现的次数。要求:用split_whitespace()分割、map转小写、fold构建 HashMap。提示:HashMap的entry()API 配合or_insert()可以方便地计数。