在 Rust 中,迭代器是处理集合数据(如数组、向量、哈希表等)的核心工具,它提供了一种统一、高效且安全的方式遍历和操作元素。
与其他语言的迭代器不同,Rust 迭代器深度融合了所有权、借用机制,兼具函数式编程的灵活性与系统级语言的性能,是 Rust 零成本抽象理念的典型体现。
本文将从基础概念出发,逐步深入迭代器的用法、特性、拓展场景,结合详细示例帮助大家彻底掌握这一核心特性。
一、迭代器的核心概念
迭代器(Iterator)本质上是一个实现了 std::iter::Iterator trait 的类型,它能够依次产生序列中的元素,并在遍历结束后标记"完成"状态。其核心逻辑由 next() 方法定义,该方法返回 Option<T>:
-
当存在下一个元素时,返回
Some(元素值); -
当遍历结束时,返回
None。
Rust 迭代器的核心优势的是惰性求值:迭代器本身不会执行任何遍历操作,直到调用了"消费型方法"才会触发元素的产生和处理,这种设计能减少不必要的计算,提升性能。
二、迭代器的基本用法
2.1 三种基础迭代器类型
针对集合的不同访问需求,Rust 提供了三种默认迭代器,分别对应 不同的借用 / 所有权 语义:
1. 不可变引用迭代器(iter())
最常用的迭代器,遍历集合时 产生元素 的不可变引用(&T),不获取元素所有权,遍历后集合仍可正常使用。
rust
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 创建不可变引用迭代器
let mut iter = vec.iter();
// 手动调用 next() 遍历
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), Some(&4));
assert_eq!(iter.next(), Some(&5));
assert_eq!(iter.next(), None);
// 遍历后集合仍可使用
println!("vec 长度: {}", vec.len()); // 输出 5
}
2. 可变引用迭代器(iter_mut())
产生元素的 可变引用(&mut T),可在遍历过程中 修改集合元素,同样不获取所有权。
rust
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
// 创建可变引用迭代器
let mut iter_mut = vec.iter_mut();
// 遍历并修改元素
if let Some(num) = iter_mut.next() {
*num = 10; // 通过可变引用修改值
}
if let Some(num) = iter_mut.next() {
*num = 20;
}
println!("修改后的 vec: {:?}", vec); // 输出 [10, 20, 3, 4, 5]
}
3. 所有权转移迭代器(into_iter())
产生元素的所有权(T),遍历后 原 集合 会被消耗(所有权转移给迭代器),无法再被访问。
rust
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 创建所有权转移迭代器,vec 所有权被转移
let mut into_iter = vec.into_iter();
assert_eq!(into_iter.next(), Some(1));
assert_eq!(into_iter.next(), Some(2));
// 此时 vec 已被消耗,无法再使用
// println!("vec: {:?}", vec); // 编译错误:value borrowed here after move
}
2.2 for 循环与迭代器的关联
Rust 中的 for 循环本质上是迭代器的语法糖,它会自动调用集合的 into_iter()(或根据上下文适配 iter()/iter_mut()),并循环调用 next() 直到返回 None。
rust
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 等价于 vec.iter() 遍历,因为 for 循环默认借用集合
for num in &vec {
println!("{}", num);
}
let mut vec_mut = vec![1, 2, 3];
// 可变遍历
for num in &mut vec_mut {
*num *= 2;
}
println!("{:?}", vec_mut); // 输出 [2, 4, 6]
// 所有权转移遍历
for num in vec {
println!("{}", num);
}
// vec 已被消耗,无法再使用
}
三、迭代器适配器:链式操作的精髓
迭代器适配器(Iterator Adapter)是一类方法,它接收一个迭代器,返回一个新的迭代器。
用于对元素进行转换、过滤、截取等操作。适配器同样遵循惰性求值,只有当后续调用消费型方法时,所有适配器的逻辑才会被执行。常见的适配器包括 map、filter、take、skip 等,且支持链式调用。
3.1 常用适配器示例
rust
fn main() {
let vec = vec![1, 2, 3, 4, 5, 6, 7, 8];
// 1. map:转换元素,将每个元素乘 2
let mapped: Vec<i32> = vec.iter().map(|&x| x * 2).collect();
println!("map 结果: {:?}", mapped); // 输出 [2, 4, 6, 8, 10, 12, 14, 16]
// 2. filter:过滤元素,保留偶数
let filtered: Vec<i32> = vec.iter().filter(|&&x| x % 2 == 0).cloned().collect();
println!("filter 结果: {:?}", filtered); // 输出 [2, 4, 6, 8]
// 3. take(n):截取前 n 个元素,超出长度则取全部
let taken: Vec<i32> = vec.iter().take(3).cloned().collect();
println!("take 结果: {:?}", taken); // 输出 [1, 2, 3]
// 4. skip(n):跳过前 n 个元素,不足则返回空
let skipped: Vec<i32> = vec.iter().skip(5).cloned().collect();
println!("skip 结果: {:?}", skipped); // 输出 [6, 7, 8]
// 5. 链式调用:组合多个适配器
let result: Vec<i32> = vec.iter()
.filter(|&&x| x > 3) // 保留大于 3 的元素
.map(|&x| x * 3) // 乘 3
.take(2) // 取前 2 个
.collect();
println!("链式调用结果: {:?}", result); // 输出 [12, 15](4*3=12,5*3=15)
}
注意:collect() 是典型的消费型方法,它将迭代器产生的元素收集到指定集合中。由于 Rust 无法自动推断集合类型,需显式标注类型(如 Vec<i32>)。
3.2 适配器的惰性求值验证
为了验证惰性求值,我们可以在适配器中加入打印逻辑,观察其执行时机:
rust
fn main() {
let vec = vec![1, 2, 3];
// 仅创建适配器链,未调用消费方法,无任何输出
let adapter_chain = vec.iter().map(|&x| {
println!("处理元素: {}", x);
x * 2
});
println!("开始消费迭代器");
// 调用 collect() 消费,此时才执行 map 逻辑
let result: Vec<i32> = adapter_chain.collect();
println!("结果: {:?}", result);
}
输出结果:
Plain
开始消费迭代器
处理元素: 1
处理元素: 2
处理元素: 3
结果: [2, 4, 6]
可见,适配器逻辑仅在消费时执行,避免了无效计算。
四、消费型迭代器方法
消费型方法(Consumer)是触发迭代器执行的核心,它会消耗迭代器,返回一个非迭代器的值(如集合、数值、布尔值等)。除了前面提到的 collect(),还有以下常用消费方法:
4.1 常用消费方法示例
rust
fn main() {
let vec = vec![1, 2, 3, 4, 5];
let iter = vec.iter().cloned();
// 1. count():统计元素个数
let count = iter.clone().count();
println!("元素个数: {}", count); // 输出 5
// 2. sum():计算元素总和(需元素实现 Sum trait)
let sum: i32 = iter.clone().sum();
println!("总和: {}", sum); // 输出 15
// 3. fold():折叠操作,接收初始值和闭包,累计计算结果
// 计算 1*2*3*4*5 = 120
let product: i32 = iter.clone().fold(1, |acc, x| acc * x);
println!("乘积: {}", product); // 输出 120
// 4. for_each():对每个元素执行闭包(类似 for 循环,无返回值)
println!("for_each 处理元素:");
iter.clone().for_each(|x| println!("{}", x));
// 5. any():判断是否存在满足条件的元素,找到后立即终止遍历(短路求值)
let has_even = iter.clone().any(|x| x % 2 == 0);
println!("是否存在偶数: {}", has_even); // 输出 true
// 6. all():判断所有元素是否都满足条件,不满足则立即终止
let all_gt_zero = iter.clone().all(|x| x > 0);
println!("所有元素都大于 0: {}", all_gt_zero); // 输出 true
// 7. find():查找第一个满足条件的元素,返回 Option<T>
let first_gt_3 = iter.find(|&x| x > 3);
println!("第一个大于 3 的元素: {:?}", first_gt_3); // 输出 Some(4)
}
其中,fold() 是功能最强大的消费方法之一,sum()、count() 等方法本质上都是基于 fold() 实现的。
五、迭代器拓展:自定义迭代器与性能分析
5.1 自定义迭代器
只要实现 std::iter::Iterator trait,就能自定义迭代器。该 trait 仅需强制实现 next() 方法,其他方法(如 map、filter、count 等)都有默认实现,无需手动编写。
示例:自定义一个生成斐波那契数列的迭代器,生成前 n 项:
rust
// 定义斐波那契迭代器结构体,存储当前状态
struct Fibonacci {
a: u32,
b: u32,
remaining: usize, // 剩余生成次数
}
// 实现构造方法,初始化迭代器
impl Fibonacci {
fn new(count: usize) -> Self {
Fibonacci {
a: 0,
b: 1,
remaining: count,
}
}
}
// 实现 Iterator trait
impl Iterator for Fibonacci {
type Item = u32; // 迭代器产生的元素类型
// 核心方法:生成下一个元素
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let result = self.a;
// 更新斐波那契状态:下一项 = a + b,a 变为原来的 b,b 变为新值
let next_b = self.a + self.b;
self.a = self.b;
self.b = next_b;
self.remaining -= 1;
Some(result)
}
}
fn main() {
// 创建斐波那契迭代器,生成前 10 项
let fib = Fibonacci::new(10);
// 消费迭代器并收集结果
let fib_vec: Vec<u32> = fib.collect();
println!("斐波那契前 10 项: {:?}", fib_vec);
// 输出:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}
5.2 迭代器的性能优势
Rust 迭代器是"零成本抽象",即编译后生成的汇编代码与手动编写的循环几乎一致,性能无损耗。这是因为:
-
迭代器的方法均为内联函数,编译器可进行充分优化;
-
惰性求值避免了中间集合的创建,减少内存开销;
-
迭代器不依赖运行时反射,所有类型检查在编译期完成。
对比手动循环与迭代器的性能(以遍历数组求和为例):
rust
fn main() {
let arr = [1; 1_000_000]; // 100 万个 1 的数组
// 手动循环求和
let mut manual_sum = 0;
for &num in &arr {
manual_sum += num;
}
// 迭代器求和
let iter_sum: i32 = arr.iter().sum();
assert_eq!(manual_sum, iter_sum);
}
通过 cargo asm 查看汇编代码可发现,两者的指令完全一致,性能毫无差异。
5.3 迭代器与所有权的深度结合
Rust 迭代器的安全性源于与所有权机制的结合,例如:
-
不可变迭代器(
iter())持有集合的不可变借用,期间无法修改集合; -
可变迭代器(
iter_mut())持有集合的可变借用,期间无法同时创建其他借用; -
所有权转移迭代器(
into_iter())确保集合仅被消费一次,避免悬垂引用。
这种设计从编译期杜绝了数据竞争和非法访问,是 Rust 安全特性的重要体现。
六、总结
Rust 迭代器是集合处理的核心工具,其惰性求值、零成本抽象、安全语义的特性,使其既能写出简洁优雅的函数式代码,又能保证系统级的性能和安全性。掌握迭代器的关键在于:
-
理解三种基础迭代器的所有权语义,根据场景选择合适的迭代方式;
-
灵活运用适配器组合操作,实现链式调用简化逻辑;
-
熟悉消费型方法,触发迭代器执行并获取结果;
-
掌握自定义迭代器的方法,适配复杂业务场景。
在实际开发中,迭代器常与 Option、Result 等类型结合(如 Option::iter()、Result::iter()),进一步拓展其应用场景。合理使用迭代器,能显著提升代码的可读性、安全性和性能。