外观
第 3 节:迭代器冷门方法
filter/map/collect 人人都认识,下面这几个"冷门但好用"的方法,能在真实项目里省下大段手写循环。
zip:两个迭代器配对 ⭐
rust
let names = ["小咪", "旺财", "圆圆"];
let scores = [9, 7];
for (name, score) in names.iter().zip(scores.iter()) {
println!("{}:{}", name, score);
}
// 小咪:9
// 旺财:7两个列表一对一对地取,短的说了算(scores 只有两个,第三轮直接结束)。做"名字对应分数"、"键对应值"的配对,一行搞定。
chain:两段接成一段 ✨
rust
let first = [1, 2];
let second = [3, 4];
let all: Vec<_> = first.iter().chain(second.iter()).collect();
// [1, 2, 3, 4]"先把这批处理了,再处理那批"——不用复制到同一个 Vec 再遍历。
fold:带着"家当"一路算 ⭐
rust
// 累加
let total = [1, 2, 3, 4].iter().fold(0, |acc, n| acc + n);
// 10
// 找最长(家当是"目前最长"——第 3 章擂台赛的迭代器版)
let longest = ["小螃蟹", "旺财", "小海马"]
.iter()
.fold("", |best, s| if s.len() > best.len() { s } else { best });
// "小螃蟹"fold(初始值, 闭包):初始值当"家当",每来一个元素,闭包更新家当。累加、计数、找最值、拼字符串——一切"边遍历边攒结果"的场景,fold 都是终极大招(比手写 let mut 循环短,还不用关心可变状态)。
partition:按条件分两堆 ⭐
rust
let numbers = [1, 2, 3, 4, 5, 6];
let (evens, odds): (Vec<&i32>, Vec<&i32>) = numbers.iter().partition(|n| *n % 2 == 0);
// evens = [2, 4, 6],odds = [1, 3, 5]"通过条件的一堆,没通过的另堆"——比如"已激活用户 / 未激活用户"。一个 partition 代替"循环 + 两个 Vec + 两个 push"。
take_while / skip_while:取到不满足为止 ✨
rust
let sorted = [1, 2, 3, 4, 5, 6];
let first_three: Vec<_> = sorted.iter().take_while(|n| **n < 4).collect();
// [1, 2, 3]——遇到不满足就停
let rest: Vec<_> = sorted.iter().skip_while(|n| **n < 4).collect();
// [4, 5, 6]——跳过不满足的,从满足的才开始和 filter 的区别:take_while 遇到第一个不满足就停(适合有序数据),filter 从头筛到尾。处理"前缀""日志文件开头跳过注释行"等场景,它是主角。
scan:比 fold 更灵活(可以提前停) ✨
rust
let sums: Vec<_> = [1, 2, 3, 4].iter().scan(0, |acc, n| {
*acc += n;
if *acc > 5 {
None // 返回 None 就停
} else {
Some(*acc) // 返回 Some 继续
}
}).collect();
// [1, 3]——累积到超过 5 就停scan 是"能喊停的 fold":积累过程中想提前结束(比如"累到超过预算就停"),用它。
记忆口诀
zip 配对,chain 接龙,fold 攒家当,partition 分堆,take_while 取前缀,scan 能喊停。 看到"手写循环 + 一个临时变量",先想想有没有迭代器方法替你干。
常见误用
- partition 的类型标注:
iter()给的是引用(&i32),类型写成Vec<&i32>(要拥有值,用into_iter()) - take_while 当 filter 用:数据无序时,
take_while会"半路截断"——有序数据才用它 - fold 的闭包忘写
acc参数:fold(0, |acc, n| ...)两个参数,别只写一个