前言:为什么一定要学迭代器?
但凡你问一位 Rust 老手:Rust 最重要、最常用、最精髓的语法是什么?
答案永远是:迭代器(Iterator)。
Rust 中几乎所有集合:数组、动态数组 Vec、哈希表 HashMap、范围序列 1...10,全部都离不开迭代器。迭代器让我们不需要关心下标、开始结束位置、遍历规则,只关心元素本身。
简洁、安全、高性能、函数式写法,这就是 Rust 迭代器。
一、for 循环本质:迭代器语法糖
1. 其他语言的循环(靠下标)
例如 JS,必须手动控制索引、长度、边界:
javascript
let arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
缺点:要自己管理下标,容易越界、写错索引。
2. Rust 的 for 循环(无下标、纯迭代)
rust
let arr = [1, 2, 3];
for v in arr {
println!("{}",v);
}
Rust 没有下标,直接遍历元素。
重点:Rust 的 for 循环本质就是迭代器的语法糖。
数组本身不是迭代器,但是数组实现了 IntoIterator 特征,所以可以自动转为迭代器。
不止数组,这种写法都可以:
rust
for i in 1..10 {
println!("{}", i);
}
二、迭代器最大特点:惰性初始化
Rust 的迭代器是**惰性(lazy)**的:
不使用、不遍历、不消费,就不会执行任何逻辑。
rust
let v1 = vec![1, 2, 3];
// 仅仅创建迭代器,什么都不发生
let v1_iter = v1.iter();
// 只有 for 遍历的时候,迭代器才开始工作
for val in v1_iter {
println!("{}", val);
}
优点:创建迭代器零开销,不占用多余性能。
三、迭代器的核心:Iterator 特征与 next() 方法
1. Iterator 源码
rust
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
只要实现这个特征,就是迭代器。
唯一必须实现的方法:next()。
2. next() 作用
- 每次调用取出一个元素
- 有值返回
Some(值) - 遍历结束返回
None - 会改变迭代器内部位置,所以迭代器必须是 mut
rust
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);
}
3. for 循环本质(手动模拟)
for 就是不断调用 next,直到 None 跳出循环:
rust
let values = vec![1, 2, 3];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
match iter.next() {
Some(x) => { println!("{}", x); },
None => break,
}
},
};
result
}
四、三种迭代器生成方式:iter / iter_mut / into_iter
这三个是新手最容易混淆的,我直白总结:
- into_iter:拿走所有权
- iter:不可变借用(只读)
- iter_mut:可变借用(可修改)
rust
fn main() {
// 1. into_iter:拿走所有权
let values = vec![1, 2, 3];
for v in values.into_iter() {
println!("{}", v)
}
// 报错!所有权已经转移
// println!("{:?}",values);
// 2. iter:不可变借用
let values = vec![1, 2, 3];
let _values_iter = values.iter();
println!("{:?}", values); // 还能使用
// 3. iter_mut:可变借用,修改元素
let mut values = vec![1, 2, 3];
let mut values_iter_mut = values.iter_mut();
if let Some(v) = values_iter_mut.next() {
*v = 0;
}
println!("{:?}", values); // [0, 2, 3]
}
返回值区别
iter()→Some(&T)iter_mut()→Some(&mut T)into_iter()→Some(T)
五、Iterator vs IntoIterator(彻底分清)
- Iterator:迭代器本身的特征,必须有 next(),能迭代
- IntoIterator:普通集合转换成迭代器的特征
一句话:
Iterator 是迭代器;IntoIterator 是能变成迭代器。
六、消费者适配器 & 迭代器适配器
1. 消费者适配器(消耗迭代器)
内部调用 next(),直接把迭代器用完,返回一个结果。
常见:sum、max、min、count、collect、fold
rust
fn main() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();
assert_eq!(total, 6);
// 报错,迭代器已经被 sum 消耗
// println!("{:?}",v1_iter);
}
2. 迭代器适配器(生成新迭代器)
不消耗迭代器,返回新迭代器,并且全部是惰性的。
常见:map、filter、zip、skip、enumerate
注意:适配器必须搭配消费者才能生效!
rust
// 警告:无任何效果,惰性迭代器
v1.iter().map(|x| x + 1);
// 必须 collect 收尾
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
七、常用迭代器方法实战
1. collect:收集为集合
collect 超级强大,能把迭代器转为 Vec、HashMap 等。
rust
use std::collections::HashMap;
fn main() {
let names = ["sunface", "sunfei"];
let ages = [18, 18];
let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect();
println!("{:?}",folks);
}
2. filter:过滤元素
rust
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()
}
3. zip:合并两个迭代器
把两个迭代器一一配对成元组。
4. enumerate:带索引遍历
rust
let v = vec![1u64, 2, 3, 4, 5, 6];
for (i,v) in v.iter().enumerate() {
println!("第{}个值是{}",i,v)
}
5. 链式调用(经典写法)
rust
let v = vec![1u64, 2, 3, 4, 5, 6];
let val = v.iter()
.enumerate()
.filter(|&(idx, _)| idx % 2 == 0)
.map(|(_, val)| val)
.fold(0u64, |sum, acm| sum + acm);
println!("{}", val);
八、手动实现自己的迭代器
只要实现 Iterator 特征,就能自定义迭代器。
示例:计数器迭代器
rust
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
// 实现迭代器
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
}
}
}
复杂链式调用演示:
rust
let sum: u32 = Counter::new()
.zip(Counter::new().skip(1))
.map(|(a, b)| a * b)
.filter(|x| x % 3 == 0)
.sum();
assert_eq!(18, sum);
九、迭代器性能:零成本抽象
很多人以为迭代器慢,实际比手写 for 下标循环更快。
官方bench测试结论:
- 迭代器没有运行时开销
- 编译器自动优化:循环展开、向量化、消除边界检查
- 属于 Rust 零成本抽象
结论:放心用迭代器,简洁又快。
十、全文终极总结(背诵版)
- for 循环是迭代器语法糖,本质不断调用 next()
- 迭代器惰性执行,不消费不运行
- Iterator 特征:必须实现 next(),返回 Option
- 三种迭代:into_iter(所有权)、iter(只读)、iter_mut(修改)
- 适配器:map/filter/zip/enumerate,惰性、生成新迭代器
- 消费者:sum/collect/fold,消耗迭代器、出结果
- collect 需要手动标注类型,用途极广
- 迭代器零成本抽象,性能媲美手写循环
- 自定义迭代器:实现 Iterator + 重写 next()