迭代器
迭代器是 Rust 中处理序列数据的核心抽象。与 C 风格的 for (i = 0; i < len; i++) 循环不同,Rust 的迭代器不需要手动管理索引、边界检查或循环状态——你只需描述"对每个元素做什么",而不是"怎么遍历"。这种抽象不仅让代码更简洁,而且由于零成本抽象原则,迭代器在运行时与等价的手写循环效率相同甚至更高。
迭代器是惰性的: 创建一个迭代器不会执行任何操作,只有调用消费它的方法时,迭代逻辑才真正运行。
Iterator trait 与 next 方法
一个类型只要实现了 Iterator trait,它就是迭代器
迭代器的核心定义来自标准库中的 Iterator trait:
pub trait Iterator {
type Item; // 关联类型: 每次"吐出"的元素类型
fn next(&mut self) -> Option<Self::Item>; // 核心方法: 取下一个元素
// ... 数十个有默认实现的方法(map、filter、sum 等)
}只要实现了 next 方法,编译器就会自动"赠送"你几十个方法——map、filter、sum、collect 等都是标准库基于 next 提供的默认实现。
手动调用 next 方法时,必须将迭代器声明为 mut,因为每次调用都会修改迭代器内部记录的"当前位置":
fn main() {
let arr = [1, 2, 3];
let mut arr_iter = arr.into_iter();
assert_eq!(arr_iter.next(), Some(1));
assert_eq!(arr_iter.next(), Some(2));
assert_eq!(arr_iter.next(), Some(3));
assert_eq!(arr_iter.next(), None); // 迭代结束
}每次调用
next都会消耗一个元素。迭代完成后只能得到None,无法"倒回"。
for 循环与迭代器
Rust 的 for 循环只是迭代器的语法糖,编译器会将它展开为对迭代器 next 方法的循环调用:
let values = vec![1, 2, 3];
for v in values {
println!("{}", v);
}
// for 循环的本质展开形式:
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
match iter.next() {
Some(x) => println!("{}", x),
None => break,
}
},
};
result
}这解释了一个常见问题: 数组本身不是迭代器,但可以在 for 循环中直接使用。原因在于数组实现了 IntoIterator trait——for 循环会自动调用 into_iter() 将其转换为迭代器。
Iterator 与 IntoIterator 的关系:
| Trait | 含义 | 必须实现的方法 |
|---|---|---|
Iterator | 就是迭代器,能调用 next | next() |
IntoIterator | 能转换为迭代器 | into_iter() |
迭代器本身也实现了 IntoIterator(直接返回自身),因此以下写法完全合法:
let values = vec![1, 2, 3];
// 无论写多少个 into_iter(),都能正常编译并运行
for v in values.into_iter().into_iter().into_iter() {
println!("{}", v);
}产生迭代器的三种方式
大多数集合类型提供三种方法来产生迭代器,区别在于对原集合所有权的处理:
| 方法 | 产生元素类型 | 对原集合的影响 | 等价的 for 写法 |
|---|---|---|---|
iter() | &T | 借用,原集合继续有效 | for x in &vec |
iter_mut() | &mut T | 可变借用,可原地修改 | for x in &mut vec |
into_iter() | T | 转移所有权,原集合失效 | for x in vec |
// iter(): 不可变借用,原集合依然有效
let v = vec![1, 2, 3];
let count = v.iter().count();
println!("v 依然可用: {:?}", v); // ✅
// iter_mut(): 可变借用,原地修改元素
let mut v1 = vec![1, 2, 3];
v1.iter_mut().for_each(|x| *x *= 2);
println!("原地翻倍: {:?}", v1); // [2, 4, 6]
// into_iter(): 转移所有权
let v2 = vec![1, 2, 3];
let v2_owned: Vec<i32> = v2.into_iter().map(|x| x + 10).collect();
// println!("{:?}", v2); // ❌ v2 已被移动能产生迭代器的常见类型
几乎所有"装着一堆东西"的类型都可以转换为迭代器:
- 集合类型:
Vec<T>、HashMap<K,V>、BTreeMap<K,V>、HashSet<T>、原生数组[T; N]、切片&[T] - 字符串:
.chars()(Unicode 字符)、.bytes()(原始字节)、.lines()(按行) - 范围:
(0..10)和(0..=10)本身就是迭代器 - Option / Result:
Some(5).iter()产生只吐出一次5的迭代器 - IO 流:
BufReader::lines()惰性按行读取文件,不需要一次性加载到内存
迭代器适配器
迭代器适配器接收一个迭代器,返回一个新的迭代器,本身不做任何计算(惰性)。必须在最后调用消费适配器才会触发实际执行:
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1);
// ⚠️ 编译器警告: unused `Map` that must be used
// 提示: iterators are lazy and do nothing unless consumed加上消费适配器 collect 后才真正执行:
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); // ✅ [2, 3, 4]常用迭代器适配器:
| 方法 | 说明 |
|---|---|
map(f) | 对每个元素做变换,返回新迭代器 |
filter(p) | 保留满足条件的元素 |
enumerate() | 带索引遍历,得到 (index, item) |
zip(other) | 将两个迭代器压缩成 (a, b) 元组迭代器 |
chain(other) | 连接两个迭代器,顺序遍历 |
flat_map(f) | map 后展平一层(等价于 .map(f).flatten()) |
flatten() | 展平嵌套迭代器(Vec<Vec<T>> → 单层) |
take(n) | 只取前 n 个元素 |
skip(n) | 跳过前 n 个元素 |
peekable() | 可以 .peek() 而不消耗元素 |
cloned() | 将 &T 迭代器转为 T 迭代器(需 Clone) |
copied() | 将 &T 迭代器转为 T 迭代器(需 Copy) |
闭包可以捕获外部环境,这是迭代器适配器最强大的特性之一:
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter().filter(|s| s.size == shoe_size).collect()
// ^^^^^^^^^^^^^^^ 捕获了外部变量 shoe_size
}链式调用示例:
let v = vec![1, 2, 3, 4, 5, 6];
// enumerate + filter + map 链式组合
let result: Vec<String> = v.iter()
.enumerate()
.filter(|(_, x)| *x % 2 == 0)
.map(|(i, x)| format!("索引{i}: {x}"))
.collect();
// zip: 合并两个迭代器
use std::collections::HashMap;
let names = ["sunface", "sunfei"];
let ages = [18u32, 20];
let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect();
// {"sunface": 18, "sunfei": 20}
// flat_map: 展开嵌套
let words = vec!["hello world", "foo bar"];
let tokens: Vec<&str> = words.iter().flat_map(|s| s.split(' ')).collect();
// ["hello", "world", "foo", "bar"]消费适配器
消费适配器在内部调用 next 方法,获取迭代器的所有权并消耗它,最终返回一个具体的值:
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum(); // sum 获取了 v1_iter 的所有权
println!("{:?}", v1); // ✅ v1 只是被借用,仍可使用
// println!("{:?}", v1_iter); // ❌ v1_iter 已被 sum 消耗常用消费适配器:
| 方法 | 说明 |
|---|---|
collect::<Vec<_>>() | 收集成集合(Vec、HashMap 等) |
count() | 元素总数 |
sum() / product() | 求和 / 求积 |
any(p) / all(p) | 任意 / 全部满足条件 → bool |
find(p) | 首个满足条件的元素 → Option<&T> |
position(p) | 首个满足条件的索引 → Option<usize> |
max() / min() | 最大 / 最小值 → Option<T> |
fold(init, f) | 折叠累积(类似 reduce) |
for_each(f) | 遍历并执行副作用 |
let v = vec![1, 2, 3, 4, 5];
let sum: i32 = v.iter().sum(); // 15
let has_even = v.iter().any(|x| x % 2 == 0); // true
let all_pos = v.iter().all(|x| *x > 0); // true
let first_gt3 = v.iter().find(|&&x| x > 3); // Some(&4)
let total = v.iter().fold(0, |acc, x| acc + x); // 15
// enumerate 获取索引
let v2 = vec![1u64, 2, 3, 4, 5, 6];
for (i, val) in v2.iter().enumerate() {
println!("第{}个值是{}", i, val);
}
// enumerate + filter: 每隔一个元素求和
let result = v2.iter()
.enumerate()
.filter(|&(idx, _)| idx % 2 == 0)
.map(|(_, val)| val)
.fold(0u64, |sum, x| sum + x); // 1 + 3 + 5 = 9自定义迭代器
只要为自己的类型实现 Iterator trait,它就能享用所有迭代器适配器。只需实现一个 next 方法即可,其余方法编译器自动赠送:
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
// 实现 Iterator trait
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
fn main() {
// 手动调用 next
let mut counter = Counter::new();
assert_eq!(counter.next(), Some(1));
assert_eq!(counter.next(), Some(2));
assert_eq!(counter.next(), Some(3));
assert_eq!(counter.next(), Some(4));
assert_eq!(counter.next(), Some(5));
assert_eq!(counter.next(), None);
// for 循环也可直接使用(Iterator 自动实现了 IntoIterator)
for item in Counter::new() {
println!("item = {}", item);
}
}实现了 next 之后,所有标准库方法都可以直接用。下面的示例仅实现了 next,却能链式调用 zip、map、filter、sum:
// 假设 Counter::new() 产生 [1, 2, 3, 4, 5]
// skip(1) 后产生 [2, 3, 4, 5]
// zip 合并为 [(1,2),(2,3),(3,4),(4,5)]
// map 相乘为 [2, 6, 12, 20]
// filter 取 3 的倍数 [6, 12]
// sum = 18
let sum: u32 = Counter::new() // 产生 [1, 2, 3, 4, 5]
.zip(Counter::new().skip(1)) // 产生 [(1,2),(2,3),(3,4),(4,5)]
.map(|(a, b)| a * b) // 产生 [2, 6, 12, 20]
.filter(|x| x % 3 == 0) // 产生 [6, 12]
.sum(); // 18
assert_eq!(18, sum);迭代器的性能
迭代器是 Rust 零成本抽象(zero-cost abstraction)之一: 使用高级迭代器不会引入运行时开销。编译器通过循环展开、向量化、边界检查消除等手段,使迭代器与手写 for 循环的效率相当,甚至更好:
test bench_for ... bench: 998,331 ns/iter (+/- 36,250) // 手写 for 循环
test bench_iter ... bench: 983,858 ns/iter (+/- 44,673) // 迭代器版本在 Rust 中,迭代器来自函数式编程范式,让代码意图更清晰、更易组合,同时不牺牲性能。在需要遍历集合时,优先选择迭代器而非手动索引循环。