什么是 Rust 迭代器?

Rust 的迭代器(Iterator)是一种强大的抽象,它允许你遍历集合元素而不需要关心底层数据结构的具体实现。迭代器模式是函数式编程的核心概念之一,Rust 在这方面做得特别出色。

迭代器的基本概念

在 Rust 中,任何实现了 Iterator trait 的类型都可以称为迭代器:

1
2
3
4
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}

核心方法 next() 返回 Option<Item>,当迭代完成时返回 None

创建迭代器的方法

iter() - 不可变引用迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
let vec = vec![1, 2, 3, 4, 5];

// 使用 iter() 创建迭代器
let mut iter = vec.iter();

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);
}

iter_mut() - 可变引用迭代

1
2
3
4
5
6
7
8
9
10
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];

// 使用 iter_mut() 创建可变迭代器
for item in vec.iter_mut() {
*item *= 2;
}

assert_eq!(vec, vec![2, 4, 6, 8, 10]);
}

into_iter() - 获取所有权迭代

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let vec = vec![1, 2, 3, 4, 5];

// 使用 into_iter() 消耗原集合
let sum: i32 = vec.into_iter().sum();

// 此时 vec 已被移动,无法再使用
// println!("{:?}", vec); // 编译错误!

assert_eq!(sum, 15);
}

常用迭代器方法详解

map() - 转换元素

map() 对迭代器中的每个元素应用一个闭包,将元素从一种类型转换为另一种类型。它是惰性适配器——调用 map() 本身不会执行任何计算,只有在消费迭代器时(如 collect()for 循环)才会真正应用闭包。

类型签名: fn map<B, F>(self, f: F) -> Map<Self, F> where F: FnMut(Self::Item) -> B

要点:

  • 闭包的参数类型取决于迭代器产生的元素类型(iter() 产生 &Tinto_iter() 产生 T
  • map() 不会改变迭代器的长度——每个输入元素恰好产生一个输出元素
  • 常见用途:类型转换、字段提取、数值变换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 将每个元素乘以 2
// map() 接收一个闭包作为参数:
// |x| x * 2
// - x: 对迭代器中每个元素的引用
// - 返回值: 转换后的新元素
// - 注意:这里 x 是 &i32 类型,乘法会自动解引用
let doubled: Vec<i32> = numbers.iter()
.map(|x| x * 2)
.collect();

assert_eq!(doubled, vec![2, 4, 6, 8, 10]);

// 复杂转换:将数字格式化为字符串
// 闭包参数 |x| 中的 x 是 &i32
// format! 宏将数字转换为 String
let strings: Vec<String> = numbers.iter()
.map(|x| format!("Number: {}", x))
.collect();

assert_eq!(strings, vec!["Number: 1", "Number: 2", "Number: 3", "Number: 4", "Number: 5"]);
}

filter() - 过滤元素

filter() 保留满足条件的元素,丢弃不满足条件的元素。闭包返回 true 时保留元素,返回 false 时丢弃。同样是惰性操作——调用 filter() 不会遍历集合。

类型签名: fn filter<P>(self, predicate: P) -> Filter<Self, P> where P: FnMut(&Self::Item) -> bool

要点:

  • 闭包接收的是元素的引用&Self::Item),而非元素本身——这是设计决定,避免消费元素
  • |&x| 模式用于解构引用,让闭包体中直接使用值而非引用
  • filter() 不会改变元素本身,只决定保留或丢弃
  • 常见用途:条件筛选、空值过滤、范围限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 过滤偶数
// filter() 接收一个返回 bool 的闭包作为参数:
// |&x| x % 2 == 0
// - &x: 对每个元素的引用解构,x 是 i32 类型
// - 返回值: bool,true 表示保留该元素,false 表示丢弃
// - 注意:|&x| 是 |x| *x 的语法糖,用于解构引用
let evens: Vec<i32> = numbers.iter()
.filter(|&x| x % 2 == 0)
.copied() // 将 &i32 转换为 i32
.collect();

assert_eq!(evens, vec![2, 4, 6, 8, 10]);

// 过滤长度大于 3 的字符串
// filter() 接收闭包 |word| word.len() > 3
// - word: &str 类型,对字符串切片的引用
// - 返回值: bool,判断字符串长度是否大于 3
let words = vec!["a", "the", "is", "rust", "code"];
let long_words: Vec<&str> = words.iter()
.filter(|word| word.len() > 3)
.copied()
.collect();

assert_eq!(long_words, vec!["rust", "code"]);
}

filter_map() - 过滤并转换

filter_map()filter()map() 的组合:闭包返回 Some(value) 时保留并转换元素,返回 None 时丢弃元素。比先 filter()map() 更简洁高效,因为只需一次遍历。

类型签名: fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> where F: FnMut(Self::Item) -> Option<B>

要点:

  • 适用于“可能失败的转换”场景,如字符串解析、类型转换、查找操作
  • filter_map(|x| x) 是过滤 Option 值的惯用写法(flatten() 也可)
  • 比单独的 filter().map() 避免了额外的迭代器适配器开销
  • 可以改变元素类型,而 filter() 只能保留原类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn main() {
let strings = vec!["1", "2", "abc", "3", "4", "def"];

// 解析数字,忽略非数字
// filter_map() 接收一个返回 Option<T> 的闭包:
// |s| s.parse().ok()
// - s: &str 类型,字符串切片的引用
// - s.parse(): 尝试解析为 i32,返回 Result<i32, ParseIntError>
// - .ok(): 将 Result 转换为 Option,成功返回 Some(value),失败返回 None
// - filter_map 会自动过滤掉 None,只保留 Some 中的值
let numbers: Vec<i32> = strings.iter()
.filter_map(|s| s.parse().ok())
.collect();

assert_eq!(numbers, vec![1, 2, 3, 4]);

// 处理 Option:过滤掉 None
// filter_map(|x| x) 中的 x 是 Option<i32>
// 直接返回 x 会过滤掉 None,只保留 Some 中的值
let maybe_numbers = vec![Some(1), None, Some(2), Some(3), None];
let numbers: Vec<i32> = maybe_numbers.into_iter()
.filter_map(|x| x)
.collect();

assert_eq!(numbers, vec![1, 2, 3]);
}

fold() - 累积计算

fold() 从一个初始值开始,依次对每个元素应用闭包,将结果累积到最终值。它是迭代器最通用的消费方法——sum()product()count() 等都可以用 fold() 实现。

类型签名: fn fold<B, F>(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B

要点:

  • 第一个参数是初始累积值,决定最终结果的类型
  • 闭包的第一个参数 acc 是当前累积值,第二个参数是迭代元素
  • reduce() 的区别:fold() 需要显式提供初始值,可以改变类型;reduce() 用第一个元素作初始值,类型不变
  • 空迭代器对 fold() 安全——直接返回初始值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 求和
// fold() 接收两个参数:
// 1. 初始值: 0
// 2. 闭包: |acc, &x| acc + x
// - acc: 累积值,类型为 i32
// - &x: 当前元素的引用,解构为 i32
// - 返回值: 新的累积值
let sum = numbers.iter()
.fold(0, |acc, &x| acc + x);

assert_eq!(sum, 15);

// 连接字符串
// fold() 参数:
// 1. 初始值: String::new() (空字符串)
// 2. 闭包: |mut acc, word| { acc.push_str(word); acc }
// - acc: 累积值,类型为 String (可变)
// - word: &str 类型,字符串切片的引用
// - 返回值: 更新后的累积值
let words = vec!["Hello", " ", "World", "!"];
let sentence = words.iter()
.fold(String::new(), |mut acc, word| {
acc.push_str(word);
acc
});

assert_eq!(sentence, "Hello World!");

// 计算阶乘
// fold() 参数:
// 1. 初始值: 1
// 2. 闭包: |acc, x| acc * x
// - acc: 累积值,类型为 i32
// - x: 当前元素,类型为 i32
let factorial = (1..=5).fold(1, |acc, x| acc * x);
assert_eq!(factorial, 120);
}

reduce() - 归约操作

reduce()fold() 类似,但使用迭代器的第一个元素作为初始累积值,因此不需要显式提供初始值。返回 Option<T>——空迭代器返回 None

类型签名: fn reduce<F>(self, f: F) -> Option<Self::Item> where F: FnMut(Self::Item, Self::Item) -> Self::Item

要点:

  • 累积值的类型与元素类型相同(无法像 fold() 那样改变类型)
  • 空迭代器返回 None,需要用 unwrap() 或模式匹配处理
  • 适合同类型归约:求和、求最大值、字符串拼接等
  • 如果需要改变累积值类型,请使用 fold() 而非 reduce()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 求和
// reduce() 接收一个闭包作为参数:
// |acc, &x| acc + x
// - acc: 累积值,类型为 &i32 (第一个元素作为初始值)
// - &x: 当前元素的引用,解构为 i32
// - 返回值: 新的累积值 &i32
// 注意:reduce() 第一个元素作为初始值,不需要单独提供
let sum = numbers.iter()
.reduce(|acc, &x| acc + x)
.unwrap(); // 空集合会返回 None,所以用 unwrap()

assert_eq!(sum, 15);

// 找最大值
// reduce() 参数:
// |acc, &x| if x > *acc { &x } else { acc }
// - acc: &i32,当前找到的最大值
// - &x: &i32,当前元素
// - 返回值: &i32,较大的那个元素的引用
// - *acc: 解引用,获取 i32 值进行比较
let max = numbers.iter()
.reduce(|acc, &x| if x > *acc { &x } else { acc })
.unwrap();

assert_eq!(*max, 5);

// 空集合返回 None
let empty: Vec<i32> = vec![];
let result = empty.iter().reduce(|acc, x| acc + x);
assert_eq!(result, None);
}

take() - 取前 n 个元素

take() 从迭代器开头取指定数量的元素,返回一个最多包含 n 个元素的新迭代器。对于有限迭代器,如果元素少于 n,则返回所有元素;对于无限迭代器,take() 限制为恰好 n 个。

类型签名: fn take(self, n: usize) -> Take<Self>

要点:

  • 常用于配合无限迭代器生成有限序列,如 0.. 是无限的,加上 .take(5) 就是有限的 [0,1,2,3,4]
  • take(0) 返回一个空迭代器
  • skip() 相对:take 取开头,skip 跳过开头
  • 惰性操作:调用 take() 不消费任何元素,只创建新迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 取前 3 个
let first_three: Vec<i32> = numbers.iter()
.take(3)
.copied()
.collect();

assert_eq!(first_three, vec![1, 2, 3]);

// 无限序列取前 5 个
let infinite = (0..).take(5);
let result: Vec<i32> = infinite.collect();

assert_eq!(result, vec![0, 1, 2, 3, 4]);
}

skip() - 跳过前 n 个元素

skip() 跳过迭代器开头的 n 个元素,返回从第 n+1 个元素开始的迭代器。如果迭代器元素少于 n,则返回空迭代器。

类型签名: fn skip(self, n: usize) -> Skip<Self>

要点:

  • skip(0) 返回一个等价的迭代器(不跳过任何元素)
  • take() 相对:take 取开头,skip 跳过开头
  • 变体 skip_while() 跳过满足条件的连续元素,一旦遇到不满足条件的元素就停止跳过
  • 适用于分页、跳过表头、跳过前置条件等场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 跳过前 3 个,取后面的
let rest: Vec<i32> = numbers.iter()
.skip(3)
.copied()
.collect();

assert_eq!(rest, vec![4, 5, 6, 7, 8, 9, 10]);

// 跳过满足条件的元素
let skip_even: Vec<i32> = numbers.iter()
.skip_while(|&x| x % 2 == 1) // 跳过奇数
.copied()
.collect();

assert_eq!(skip_even, vec![2, 3, 4, 5, 6, 7, 8, 9, 10]);
}

take_while() - 取满足条件的元素

take_while() 从开头开始取元素,只要闭包返回 true 就继续,一旦遇到返回 false 的元素就立即停止,不再处理后续元素。

类型签名: fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P> where P: FnMut(&Self::Item) -> bool

要点:

  • filter() 的区别:filter 检查所有元素,take_while 只检查连续元素
  • 一旦遇到不满足条件的元素,迭代立即终止——后续元素不会被处理
  • 适用于“取到某个临界点为止”的场景,如前缀序列、连续满足条件的部分
  • 变体 take(n)take_while 的特例:按索引而非条件截断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 1, 2, 3];

// 取连续小于 4 的元素
// take_while() 接收一个返回 bool 的闭包:
// |&x| x < &4
// - &x: 对元素的引用解构,x 是 i32 类型
// - 返回值: bool,true 表示继续取,false 表示停止
// - 注意:一旦遇到不满足条件的元素,后续所有元素都会被丢弃
let result: Vec<i32> = numbers.iter()
.take_while(|&x| x < &4)
.copied()
.collect();

assert_eq!(result, vec![1, 2, 3]);

// 字符串示例
// take_while() 参数:
// |word| word.starts_with('a')
// - word: &str 类型,字符串切片的引用
// - 返回值: bool,判断是否以 'a' 开头
let words = vec!["apple", "banana", "cherry", "date"];
let a_words: Vec<&str> = words.iter()
.take_while(|word| word.starts_with('a'))
.copied()
.collect();

assert_eq!(a_words, vec!["apple"]);
}

chain() - 连接多个迭代器

chain() 将两个迭代器首尾相连,返回一个先遍历第一个迭代器、再遍历第二个迭代器的新迭代器。它实现了迭代器的拼接操作。

类型签名: fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where U: IntoIterator<Item = Self::Item>

要点:

  • 接收的是 IntoIterator,所以可以传入 Vec数组Range 等,自动调用 into_iter()
  • 元素类型必须一致——chain 不会做类型转换
  • 可以链式调用多次:.chain(a).chain(b).chain(c)
  • chain() 类似的扁平化操作还有 flat_map(),但 flat_map 会把闭包返回的每个迭代器展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];

// 连接两个向量
let combined: Vec<i32> = vec1.iter()
.chain(vec2.iter())
.copied()
.collect();

assert_eq!(combined, vec![1, 2, 3, 4, 5, 6]);

// 连接多个
let vec3 = vec![7, 8];
let all: Vec<i32> = vec1.iter()
.chain(vec2.iter())
.chain(vec3.iter())
.copied()
.collect();

assert_eq!(all, vec![1, 2, 3, 4, 5, 6, 7, 8]);
}

zip() - 组合迭代器

zip() 将两个迭代器的元素按索引配对,生成一个元组迭代器。当两个迭代器长度不同时,以较短的为准,长的那部分多余元素被丢弃。

类型签名: fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where U: IntoIterator

要点:

  • 返回类型是 (Self::Item, U::Item) 元组的迭代器
  • 长度不一致时不会报错,只会丢失较长迭代器的多余元素——需要自行保证长度匹配
  • 常用于“并行遍历”多个集合:配对姓名和年龄、键和值、坐标 x 和 y 等
  • 如果需要同时遍历多个,用 multiunzip() 扩展 trait(需要 itertools crate)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
fn main() {
let names = vec!["Alice", "Bob", "Charlie"];
let ages = vec![25, 30, 35];

// 组合两个迭代器
// zip() 接收另一个迭代器作为参数:
// .zip(ages.iter())
// - ages.iter(): 生成第二个迭代器
// - 返回值: 迭代器,每个元素是 (&str, &i32) 元组
// - map() 参数: |(&name, &age)| (name, age)
// - (&name, &age): 解构元组引用,name 是 &str,age 是 &i32
// - 返回值: (&str, i32) 元组 (age 被解引用)
let people: Vec<(&str, i32)> = names.iter()
.zip(ages.iter())
.map(|(&name, &age)| (name, age))
.collect();

assert_eq!(people, vec![
("Alice", 25),
("Bob", 30),
("Charlie", 35)
]);

// 处理不同长度:zip 以较短的为准
let short = vec![1, 2];
let long = vec![10, 20, 30, 40];
let zipped: Vec<(i32, i32)> = short.iter()
.zip(long.iter())
.map(|(&a, &b)| (a, b))
.collect();

assert_eq!(zipped, vec![(1, 10), (2, 20)]);
}

enumerate() - 添加索引

enumerate() 为迭代器的每个元素添加一个从 0 开始的序号,返回 (索引, 元素) 元组的迭代器。这是 Rust 中最常用的获取索引的方式。

类型签名: fn enumerate(self) -> Enumerate<Self>

要点:

  • 索引从 0 开始,依次递增
  • 返回的元组类型是 (usize, Self::Item)——索引是 usize,元素保持原类型
  • zip() 结合可以与其他序列配对,如 enumerate().zip(ids.iter())
  • 常用于需要下标的场景:打印行号、基于索引查找、条件判断时需要序号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
let fruits = vec!["apple", "banana", "cherry"];

// 获取索引和值
for (index, fruit) in fruits.iter().enumerate() {
println!("{}: {}", index, fruit);
}

// 输出:
// 0: apple
// 1: banana
// 2: cherry

// 转换为带索引的元组
let indexed: Vec<(usize, &str)> = fruits.iter()
.enumerate()
.map(|(i, &f)| (i, f))
.collect();

assert_eq!(indexed, vec![(0, "apple"), (1, "banana"), (2, "cherry")]);
}

rev() - 反转迭代器

rev() 返回一个反向遍历的迭代器。注意:只有实现了 DoubleEndedIterator trait 的迭代器才能反转——这要求迭代器可以从两端消费。

类型签名: fn rev(self) -> Rev<Self> where Self: DoubleEndedIterator

要点:

  • 并非所有迭代器都可反转:Range<i32> 可以,但 HashSet 的迭代器不行
  • 调用 rev() 时如果迭代器不支持,会导致编译错误
  • 反转后再反转会回到原始顺序
  • enumerate() 结合可实现反向索引:(0..n).rev() 产生 [n-1, n-2, ..., 0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 反向迭代
let reversed: Vec<i32> = numbers.iter()
.rev()
.copied()
.collect();

assert_eq!(reversed, vec![5, 4, 3, 2, 1]);

// 反向枚举
for (index, value) in numbers.iter().rev().enumerate() {
println!("{}: {}", index, value);
}

// 输出:
// 0: 5
// 1: 4
// 2: 3
// 3: 2
// 4: 1
}

flat_map() - 扁平化映射

flat_map() 对每个元素应用闭包,将返回的多个迭代器合并成一个扁平的迭代器。本质上是 map().flatten() 的组合,但更高效(只遍历一次)。

类型签名: fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> where F: FnMut(Self::Item) -> U, U: IntoIterator

要点:

  • 闭包返回的是一个 IntoIterator,可以是 Vec切片Range、字符串等
  • flatten() 可以单独使用,作用是把嵌套的迭代器展平
  • 常见用途:分割字符串、展开嵌套结构、生成多个结果
  • map() 的区别:map 的闭包返回单个值,flat_map 的闭包返回多个值(迭代器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn main() {
let nested = vec![vec![1, 2], vec![3, 4], vec![5, 6]];

// 扁平化嵌套向量
// flat_map() 接收一个返回迭代器的闭包:
// |vec| vec.iter()
// - vec: &Vec<i32> 类型,对内层向量的引用
// - 返回值: 迭代器 (vec.iter() 返回 Iter<'_, i32>)
// - flat_map 会将多个迭代器合并为一个
let flat: Vec<i32> = nested.iter()
.flat_map(|vec| vec.iter())
.copied()
.collect();

assert_eq!(flat, vec![1, 2, 3, 4, 5, 6]);

// 字符串分割示例
// flat_map() 参数:
// |s| s.split_whitespace()
// - s: &str 类型,字符串切片的引用
// - s.split_whitespace(): 返回 SplitWhitespace 迭代器
// - flat_map 将每个字符串分割的结果合并
let sentences = vec!["hello world", "rust programming"];
let words: Vec<&str> = sentences.iter()
.flat_map(|s| s.split_whitespace())
.collect();

assert_eq!(words, vec!["hello", "world", "rust", "programming"]);
}

cloned() - 克隆元素

cloned() 将迭代器的元素从引用类型转换为值类型,通过调用 clone() 实现。对于实现了 Copy trait 的类型,克隆是零成本操作(只是位复制)。

类型签名: fn cloned<'a, T>(self) -> Cloned<Self> where T: 'a + Clone, Self: Iterator<Item = &'a T>

要点:

  • iter() 返回 &Tcloned() 将其转换为 T(调用 clone)
  • 对于 Copy 类型(i32、char、bool 等),cloned() 等价于 copied(),没有任何运行时开销
  • 对于非 Copy 类型(如 StringVec<T>),cloned() 会进行深拷贝——注意性能
  • 另一种选择是 copied(),适用于 Copy 类型且零拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 克隆引用
let cloned: Vec<i32> = numbers.iter()
.cloned()
.collect();

assert_eq!(cloned, vec![1, 2, 3, 4, 5]);

// 对于实现了 Copy 的类型,cloned() 等同于 copied()
let copied: Vec<i32> = numbers.iter()
.copied()
.collect();

assert_eq!(copied, vec![1, 2, 3, 4, 5]);
}

collect() - 收集为集合

collect() 是迭代器最重要的消费方法之一,将迭代器的所有元素收集到一个集合类型中。它依赖 FromIterator trait 来实现各种集合类型的构造。

类型签名: fn collect<B>(self) -> B where B: FromIterator<Self::Item>

要点:

  • 必须显式指定目标类型,因为类型推断可能不明确:let v: Vec<i32> = iter.collect()
  • 支持的集合类型:Vec<T>HashSet<T>HashMap<K, V>BTreeSet<T>String(字符集合)、Result<T, E>
  • 对于需要键值对的集合(如 HashMap),迭代器需要产生 (K, V) 元组
  • collect()急切操作——会立即遍历并消费整个迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 收集为 Vec
// collect() 不接收参数,需要显式指定目标类型
// - FromIterator trait 决定了支持哪些集合类型
// - 需要配合 copied() 或 cloned() 处理引用
let vec: Vec<i32> = numbers.iter()
.filter(|&x| x % 2 == 0)
.copied()
.collect();

// 收集为 String
// String 实现了 FromIterator<char>
// - map() 将每个数字转换为 String
// - collect() 将所有 String 拼接
let string: String = numbers.iter()
.map(|x| x.to_string())
.collect();

// 收集为 HashMap
// HashMap 实现了 FromIterator<(K, V)>
// - map() 生成 (key, value) 元组
// - collect() 自动构建 HashMap
use std::collections::HashMap;
let map: HashMap<i32, String> = numbers.iter()
.map(|&x| (x, format!("num_{}", x)))
.collect();

// 收集为 HashSet
// HashSet 自动去重
use std::collections::HashSet;
let set: HashSet<i32> = vec![1, 2, 2, 3, 3, 4].iter()
.copied()
.collect();

assert_eq!(set.len(), 4); // 自动去重
}

sum()product() - 求和与求积

sum() 计算所有元素的总和,product() 计算所有元素的乘积。它们是 fold() 的特化实现,专门针对数值运算进行了优化。

类型签名:

  • fn sum<S>(self) -> S where S: Sum<Self::Item>
  • fn product<P>(self) -> P where P: Product<Self::Item>

要点:

  • 必须显式指定返回类型,因为编译器无法推断:let sum: i32 = vec.iter().sum()
  • 默认实现了 SumProduct trait 的类型包括:所有数字类型(i32、f64、BigInt 等)
  • 可以对实现了这些 trait 的自定义类型使用
  • sum() 等价于 fold(0, |acc, x| acc + x)product() 等价于 fold(1, |acc, x| acc * x)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 求和
// sum() 不接收参数,返回 T 类型
// - 需要显式指定返回类型
// - 自动处理累加逻辑
let sum: i32 = numbers.iter().sum();
assert_eq!(sum, 15);

// 求积
// product() 不接收参数,返回 T 类型
// - 需要显式指定返回类型
// - 计算所有元素的乘积
let product: i32 = numbers.iter().product();
assert_eq!(product, 120);

// 浮点数
// sum() 和 product() 支持各种数值类型
let floats = vec![1.5, 2.0, 3.5];
let float_sum: f64 = floats.iter().sum();
assert_eq!(float_sum, 7.0);
}

max()min() - 最大最小值

max()min() 返回迭代器中的最大/最小元素。它们要求元素类型实现 PartialOrd trait,以便进行大小比较。

类型签名:

  • fn max(self) -> Option<Self::Item> where Self::Item: PartialOrd
  • fn min(self) -> Option<Self::Item> where Self::Item: PartialOrd

要点:

  • 返回 Option<T> —— 空迭代器返回 None,非空返回 Some(value)
  • 使用 PartialOrd 而非 Ord,因为某些类型(如浮点数 f64)无法全序比较
  • max_by_key()min_by_key() 允许自定义比较键:按字符串长度找最长、按年龄找人最年长等
  • 对于自定义比较逻辑,max_by() 接受一个比较函数,可实现任意比较规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn main() {
let numbers = vec![3, 1, 4, 1, 5, 9, 2, 6];

// 最大值
// max() 不接收参数,返回 Option<&T>
// - 对于有序类型,比较大小
// - 空迭代器返回 None
let max = numbers.iter().max().unwrap();
assert_eq!(*max, 9);

// 最小值
// min() 不接收参数,返回 Option<&T>
let min = numbers.iter().min().unwrap();
assert_eq!(*min, 1);

// 自定义比较
// max_by_key() 接收一个闭包作为参数:
// |word| word.len()
// - word: &str 类型,字符串切片的引用
// - 返回值: usize,用于比较的键值
// - 函数会返回键值最大的元素
let words = vec!["apple", "banana", "cherry"];
let longest = words.iter().max_by_key(|word| word.len()).unwrap();
assert_eq!(*longest, "banana");
}

any()all() - 检查条件

any() 检查是否存在满足条件的元素(短路求值),all() 检查是否所有元素都满足条件。它们是短路操作:一旦结果确定就停止迭代。

类型签名:

  • fn any<P>(self, predicate: P) -> bool where P: FnMut(&Self::Item) -> bool
  • fn all<P>(self, predicate: P) -> bool where P: FnMut(&Self::Item) -> bool

要点:

  • any() 找到第一个满足条件的就返回 true,全部不满足才返回 false
  • all() 遇到第一个不满足条件的就返回 false,全部满足才返回 true
  • 短路行为带来性能优势:对于大数据集,不必遍历全部元素
  • 闭包返回类型是 bool,不是 OptionResult
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 是否存在大于 3 的数
// any() 接收一个返回 bool 的闭包:
// |&x| x > 3
// - &x: 对元素的引用解构,x 是 i32 类型
// - 返回值: bool,true 表示存在满足条件的元素
// - 短路求值:找到第一个满足条件的就返回
let has_large = numbers.iter().any(|&x| x > 3);
assert_eq!(has_large, true);

// 是否所有数都小于 10
// all() 接收一个返回 bool 的闭包:
// |&x| x < 10
// - 返回值: bool,true 表示所有元素都满足条件
// - 短路求值:找到第一个不满足的就返回 false
let all_small = numbers.iter().all(|&x| x < 10);
assert_eq!(all_small, true);

// 是否存在负数
let has_negative = numbers.iter().any(|&x| x < 0);
assert_eq!(has_negative, false);
}

find() - 查找元素

find() 从迭代器开头开始查找第一个满足条件的元素,返回 Option<&T>。找到后立即停止迭代(短路行为)。

类型签名: fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where P: FnMut(&Self::Item) -> bool

要点:

  • 返回的是元素的引用(Option<&T>),而非元素本身
  • 闭包参数是 &Self::Item,所以 iter() 产生的 &T 在闭包中变成 &&T,需要 |&&x| 解构
  • 只返回第一个匹配元素,如需所有匹配请用 filter().collect()
  • 变体 find_map() 结合了 findmap:返回第一个 Some(value),同时转换类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 查找第一个大于 3 的数
// find() 接收一个返回 bool 的闭包:
// |&&x| x > 3
// - &&x: 对引用的引用解构,x 是 i32 类型
// (第一个 & 来自 iter(),第二个 & 来自 find 的参数)
// - 返回值: bool,true 表示找到目标
// - 返回值: Option<&T>,找到返回 Some,否则 None
let found = numbers.iter().find(|&&x| x > 3);
assert_eq!(found, Some(&4));

// 查找不存在的元素
let not_found = numbers.iter().find(|&&x| x > 10);
assert_eq!(not_found, None);

// 查找字符串
// find() 参数:
// |&&word| word == "awesome"
// - &&word: &str 类型
let words = vec!["rust", "is", "awesome"];
let awesome = words.iter().find(|&&word| word == "awesome");
assert_eq!(awesome, Some(&"awesome"));
}

position() - 查找位置

position() 查找第一个满足条件的元素的索引位置,返回 Option<usize>。与 find() 类似但返回索引而非元素本身。

类型签名: fn position<P>(&mut self, predicate: P) -> Option<usize> where P: FnMut(Self::Item) -> bool

要点:

  • 返回的是索引(usize),不是元素引用
  • 闭包参数是 Self::Item(而非 &Self::Item),所以 iter() 产生的 &T 在闭包中是 &T,用 |&x| 解构
  • 只返回第一个匹配的索引,重复元素不会返回多个
  • enumerate().find() 等价,但 position() 更简洁高效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 查找第一个大于 3 的位置
// position() 接收一个返回 bool 的闭包:
// |&x| x > 3
// - &x: 对元素的引用解构,x 是 i32 类型
// - 返回值: bool,true 表示找到目标
// - 返回值: Option<usize>,元素的索引位置
let pos = numbers.iter().position(|&x| x > 3);
assert_eq!(pos, Some(3));

// 查找不存在元素的位置
let not_pos = numbers.iter().position(|&x| x > 10);
assert_eq!(not_pos, None);

// 查找重复元素
// position() 返回第一个匹配的索引
let with_duplicates = vec![1, 2, 3, 2, 4];
let first_duplicate = with_duplicates.iter()
.position(|&x| x == 2);
assert_eq!(first_duplicate, Some(1));
}

fold_while() - 条件累积

fold_while()itertools crate 提供的扩展方法,与 fold() 类似但支持提前终止。闭包返回 FoldWhile::Continue(value) 继续迭代,返回 FoldWhile::Done(value) 立即停止。

类型签名: fn fold_while<T, F>(self, init: T, f: F) -> FoldWhile<T> where F: FnMut(T, Self::Item) -> FoldWhile<T>

要点:

  • 来自 itertools crate,需要 use itertools::FoldWhile::{Continue, Done};
  • fold() 无法提前终止——即使累积值已满足条件也会遍历所有元素
  • fold_while() 在满足条件时立即停止,避免不必要的计算
  • into_inner()FoldWhile 中提取最终累积值,无论是因为 Done 还是迭代结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use std::iter::FoldWhile::{Continue, Done};

fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 累加直到超过 20
// fold_while() 接收两个参数:
// 1. 初始值: 0
// 2. 闭包: |acc, &x| { ... }
// - acc: 累积值,类型为 i32
// - &x: 当前元素的引用,解构为 i32
// - 返回值: FoldWhile<i32>
// - Continue(sum): 继续迭代
// - Done(sum): 停止迭代
// - into_inner(): 从 FoldWhile 中提取最终值
let result = numbers.iter()
.fold_while(0, |acc, &x| {
let sum = acc + x;
if sum > 20 {
Done(sum)
} else {
Continue(sum)
}
});

assert_eq!(result.into_inner(), 21);
}

scan() - 带状态的转换

scan()fold() 类似,但闭包可以输出中间结果而非只返回最终值。它维护一个可变状态,每次迭代产生一个 Option 输出——Some(value) 输出值,None 停止迭代。

类型签名: fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F> where F: FnMut(&mut St, Self::Item) -> Option<B>

要点:

  • 状态是 &mut 可变引用,闭包可以修改它
  • 返回 Option<B>Some 表示输出一个值,None 表示停止迭代
  • fold() 的区别:fold 只返回最终值,scan 输出每一步的中间结果
  • 典型用途:前缀和、运行最大值、状态机、斐波那契数列等需要跟踪状态的序列生成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 计算前缀和
// scan() 接收两个参数:
// 1. 初始状态: 0
// 2. 闭包: |state, &x| { ... }
// - state: &mut i32,可变状态引用
// - &x: 当前元素的引用,解构为 i32
// - 返回值: Option<i32>,Some 表示输出值,None 表示停止
let prefix_sum: Vec<i32> = numbers.iter()
.scan(0, |state, &x| {
*state += x;
Some(*state)
})
.collect();

assert_eq!(prefix_sum, vec![1, 3, 6, 10, 15]);

// 斐波那契数列
// scan() 参数:
// 1. 初始状态: (0, 1) 元组
// 2. 闭包: |state, _| { ... }
// - state: &mut (i32, i32),可变状态引用
// - _: (),忽略输入值
// - 返回值: Option<i32>,斐波那契数
let fib: Vec<i32> = (0..)
.scan((0, 1), |state, _| {
let next = state.0 + state.1;
*state = (state.1, next);
Some(next)
})
.take(10)
.collect();

assert_eq!(fib, vec![1, 2, 3, 5, 8, 13, 21, 34, 55, 89]);
}

cycle() - 循环迭代器

cycle() 将一个有限迭代器变为无限循环迭代器——遍历完所有元素后从头开始,永不停歇。通常与 take() 配合使用来限制重复次数。

类型签名: fn cycle(self) -> Cycle<Self> where Self: Clone

要点:

  • 要求迭代器实现 Clone——因为需要保存初始状态以便重新开始
  • 空迭代器调用 cycle() 仍然是空的(无法循环空序列)
  • 常见模式:iter.cycle().take(n) 实现重复 n 次的效果
  • 适用于轮询调度、循环缓冲、重复模式等场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let numbers = vec![1, 2, 3];

// 重复 3 次
// cycle() 不接收参数,返回一个无限循环的迭代器
// - 会不断重复原迭代器的元素
// - 通常与 take() 配合使用来限制重复次数
let repeated: Vec<i32> = numbers.iter()
.cycle()
.take(9)
.copied()
.collect();

assert_eq!(repeated, vec![1, 2, 3, 1, 2, 3, 1, 2, 3]);

// 无限循环
// cycle() 生成无限序列,需要手动控制或使用 take()
let mut iter = [1, 2].iter().cycle();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
}

chunks()windows() - 窗口操作

chunks() 将切片分割为不重叠的固定大小块,windows() 生成滑动窗口(相邻窗口有重叠)。它们是切片方法而非迭代器方法,但返回迭代器。

类型签名:

  • fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>
  • fn windows(&self, size: usize) -> Windows<'_, T>

要点:

  • chunks() 不重叠:[1,2,3,4,5].chunks(2)[[1,2], [3,4], [5]],最后一块可能不足指定大小
  • windows() 有重叠:[1,2,3,4].windows(2)[[1,2], [2,3], [3,4]],每个窗口恰好指定大小
  • windows() 产生的窗口数量 = len() - size + 1,如果 size > len 则返回空迭代器
  • 典型用途:分批处理(chunks)、移动平均/滑动计算(windows)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];

// 分块,每块 2 个元素
// chunks() 接收一个 usize 参数:
// - 2: 每块的大小
// - 返回值: 迭代器,每个元素是 &[i32] (切片引用)
// - 注意:最后一块可能小于指定大小
let chunks: Vec<&[i32]> = numbers.chunks(2).collect();
assert_eq!(chunks, vec![&[1, 2], &[3, 4], &[5, 6]]);

// 滑动窗口,窗口大小 3
// windows() 接收一个 usize 参数:
// - 3: 窗口大小
// - 返回值: 迭代器,每个元素是 &[i32] (窗口切片)
// - 注意:窗口会滑动,每个元素可能出现在多个窗口中
let windows: Vec<&[i32]> = numbers.windows(3).collect();
assert_eq!(windows, vec![&[1, 2, 3], &[2, 3, 4], &[3, 4, 5], &[4, 5, 6]]);

// 计算移动平均
// windows(3) 生成窗口切片
// map() 参数:
// |window| window.iter().sum::<i32>() as f64 / 3.0
// - window: &[i32],窗口切片
// - window.iter().sum::<i32>(): 计算窗口内元素和
// - 除以 3.0 得到平均值
let moving_avg: Vec<f64> = numbers.windows(3)
.map(|window| window.iter().sum::<i32>() as f64 / 3.0)
.collect();

assert_eq!(moving_avg, vec![2.0, 3.0, 4.0, 5.0]);
}

step_by() - 步长迭代

step_by() 从迭代器的第一个元素开始,每隔指定步长取一个元素。步长为 1 时等价于原迭代器,步长为 2 时每隔一个取一个。

类型签名: fn step_by(self, step: usize) -> StepBy<Self>

要点:

  • 始终包含第一个元素,然后按步长跳过
  • step_by(1) 等价于原迭代器,step_by(2) 取奇数位元素
  • 步长为 0 会 panic(无意义操作)
  • skip() 结合可实现“从第 n 个开始每隔 m 个取一个”的模式
  • 适用于采样、隔行处理、等间隔选取等场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 每隔 2 个取一个
// step_by() 接收一个 usize 参数:
// - 2: 步长,表示每隔几个元素取一个
// - 返回值: 迭代器,从第一个元素开始,按步长取元素
let every_other: Vec<i32> = numbers.iter()
.step_by(2)
.copied()
.collect();

assert_eq!(every_other, vec![1, 3, 5, 7, 9]);

// 从索引 1 开始,每隔 3 个
// step_by() 参数:
// - 3: 步长
// - 注意:step_by 作用于 skip 之后的结果
let skipped: Vec<i32> = numbers.iter()
.skip(1)
.step_by(3)
.copied()
.collect();

assert_eq!(skipped, vec![2, 5, 8]);
}

高级迭代器技巧

自定义迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Countdown {
count: u32,
}

impl Iterator for Countdown {
type Item = u32;

fn next(&mut self) -> Option<Self::Item> {
if self.count == 0 {
None
} else {
let current = self.count;
self.count -= 1;
Some(current)
}
}
}

fn main() {
let countdown = Countdown { count: 3 };
let result: Vec<u32> = countdown.collect();
assert_eq!(result, vec![3, 2, 1]);
}

迭代器链

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let result: Vec<i32> = (1..=10)
.filter(|x| x % 2 == 0) // [2, 4, 6, 8, 10]
.map(|x| x * x) // [4, 16, 36, 64, 100]
.filter(|x| x > &20) // [36, 64, 100]
.rev() // [100, 64, 36]
.take(2) // [100, 64]
.collect();

assert_eq!(result, vec![100, 64]);
}

性能优化:使用迭代器而非循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let numbers = vec![1, 2, 3, 4, 5];

// 迭代器方式(推荐)
let sum_iter: i32 = numbers.iter().sum();

// 传统循环方式
let mut sum_loop = 0;
for &num in &numbers {
sum_loop += num;
}

assert_eq!(sum_iter, sum_loop);

// 迭代器通常更高效,因为编译器可以更好地优化
// 并且避免了边界检查
}

总结

Rust 的迭代器系统是其最强大的特性之一:

  1. 零成本抽象:迭代器在编译时被优化为高效的代码
  2. 类型安全:编译器确保所有操作都是类型安全的
  3. 惰性求值:大多数操作是惰性的,只在需要时执行
  4. 组合性:可以轻松组合多个操作
  5. 可读性:函数式风格使代码更清晰

掌握迭代器可以让你写出更简洁、更高效、更安全的 Rust 代码。通过组合各种迭代器方法,你可以避免手动编写循环,减少错误,并让代码更具表达力。