以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正

前言

和Python类似,Rust中的迭代器也是惰性的,即在调用消费迭代器的方法之前,迭代器本身没有任何效果,迭代器不会一次性计算和生成所有元素,而是在需要时逐个生成元素

1
2
3
4
5
6
7
fn main() {
let v = vec![1, 2, 3];
let v_iter = v.iter();
for ele in v_iter { // 注意:这里的变量"ele"的类型是&i32
println!("{}", ele);
}
}

Iterator trait

在Rust中,所有迭代器都需要实现Iterator trait,且因为Iterator trait中所包含的一些默认方法会调用到next()方法,故在Rust中要求实现这个trait时必须实现next()方法,Iterator trait的定义大致如下所示

1
2
3
4
5
6
pub trait Iterator {
// 关联类型
type Item;
// Iterator trait仅要求实现next()方法
fn next(&mut self) -> Option<Self::item>;
}

next()方法的主要作用是当每次调用迭代器的next()方法时,将迭代器中的一项包装在Some()里并返回,当迭代结束时返回None

1
2
3
4
5
6
7
8
9
10
fn main() {
let v = vec![1, 2, 3];
let mut v_iter = v.iter(); // 注意: v_iter是可变的

let _a = v_iter.next(); // 变量"_a"的类型为Option<&i32>

println!("{:?}", v_iter.next());
println!("{:?}", v_iter.next());
println!("{:?}", v_iter.next()); // 最后打印的结果是None
}

需要注意的是,变量”v_iter”是mutable的,因为在用户在调用迭代器的next()方法时相当于改变迭代器中的记录位置序列的状态,可以理解成每一次调用next()方法时都消耗了迭代器中一个元素

迭代器中元素的所有权

iter()方法

在使用iter()方法时会返回一个不可变引用的迭代器,只允许对每个元素进行只读的操作,但不允许修改原始数据,通常可以用在可迭代的不可变引用类型,例如&[T]这样的类型

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

// 变量number的类型是&i32,是迭代器内元素的不可变引用
for number in numbers.iter() {
println!("Read Only: {}", number);
}

println!("{:?}", numbers);
}

注意:通过iter方法获得的是迭代器中元素的不可变引用而不是迭代器的不可变引用

into_iter()方法

在使用into_iter()方法时返回一个拥有所有权的迭代器,允许对每个元素进行拥有所有权的操作,即可以进行移动语义的操作,迭代后原始数据无法再使用,通常用于可迭代的拥有所有权的类型,例如Vec<T>

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

// 变量number的类型是i32,其拥有迭代器内元素的所有权
for number in numbers.into_iter() {
println!("Owned: {}", number);
}

// 此处会出现错误,因为numbers的所有权已被转移
println!("{:?}", numbers);
}

和iter()一样,into_iter()方法获得的也是迭代器内部元素的所有权而不是迭代器的所有权

iter_mut()方法

在使用iter_mut()方法时返回一个可变引用的迭代器,允许对每个元素进行读写操作,可以修改原始数据,适用于可迭代的可变引用类型,例如&mut [T]

1
2
3
4
5
6
7
8
9
10
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5]; // 变量numbers是可变的Vector

// 变量number的类型是&mut i32,是迭代器内元素的可变引用
for number in numbers.iter_mut() {
*number = *number + 1; // 修改原始数据
}

println!("{:?}", numbers);
}

产生迭代器方法(迭代器适配器)

enumerate()方法

enumerate()方法用于同时迭代集合中的元素和它们的索引,其返回一个产生元素和索引元组的迭代器

1
2
3
4
5
6
7
fn main() {
let fruits = vec!["apple", "banana", "cherry"];

for (index, fruit) in fruits.iter().enumerate() {
println!("Index: {}, Fruit: {}", index, fruit);
}
}

map()方法

map()方法用于将一个迭代器中的每个元素映射为另一个元素,然后返回一个产生映射结果的新迭代器,可以对集合中的每个元素应用某个函数,并在新迭代器中获取映射后的值

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

// 使用map()将每个数字平方
let squared_numbers: Vec<i32> = numbers.iter().map(|&x| x * x).collect();

println!("Original Numbers: {:?}", numbers);
println!("Squared Numbers: {:?}", squared_numbers);
}

zip()方法

zip()方法用于将两个迭代器逐对地组合,然后返回一个新的、产生元组的迭代器,其中第一个元素来自第一个迭代器,第二个元素来自第二个迭代器。可以理解成zip()将两个迭代器压缩在了一起,最后形成的元组的数量取决于两个迭代器中数量少的那个

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let names = vec!["Alice", "Bob", "Charlie", "June"]; // 两个Vector内元素数量不一致
let ages = vec![25, 30, 22];

// 使用zip()将姓名和年龄逐对组合
let combined: Vec<(&&str, &i32)> = names.iter().zip(ages.iter()).collect();

for (name, age) in combined {
println!("Name: {}, Age: {}", name, age);
}
}

fliter()方法

filter()方法用于根据特定条件过滤出符合条件的元素生成一个新的迭代器

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

// 使用filter()方法筛选出大于等于6的元素
let filtered_numbers: Vec<i32> = numbers.iter().cloned().filter(|&x| x >= limit).collect(); // 这里使用闭包捕获了环境

println!("Original Numbers: {:?}", numbers);
println!("Filtered Numbers: {:?}", filtered_numbers);
}

除以上方法外,Rust中还有flat_map()、cycle()、take()、skip()、chain()、rev()等迭代器适配器,这里就不一一举例了

消耗迭代器方法(消耗型适配器)

collect()方法

collect()方法用于从一个迭代器中收集元素并创建一个集合,如数组、向量、哈希映射等,比如下面这个创建HashMap的例子

1
2
3
4
5
6
7
8
9
10
11
use std::collections::HashMap;

fn main() {
let person_data = vec![("Alice", 25), ("Bob", 30), ("Charlie", 22)];

// 使用collect()方法将键值对收集到一个新的HashMap中
let person_map: HashMap<_, _> = person_data.into_iter().collect();

println!("{:?}", person_map);
}

也可以配合使用zip()方法一起使用

1
2
3
4
5
6
7
8
9
10
use std::collections::HashMap;

fn main() {
let keys = vec!["Alice", "Bob", "Charlie"];
let values = vec![25, 30, 22, 27]; // 此处两个Vector内元素数量不一致

let map: HashMap<_, _> = keys.iter().zip(values.iter()).collect();

println!("{:?}", map);
}

一般情况下,在使用collect()方法时需要明确指定要收集的元素类型以及要返回的集合类型,如果不明确指定类型,编译器可能无法推断出正确的类型,导致编译错误

sum()方法

sum()方法会取得迭代器的所有权,其主要的原理是反复调用迭代器的next()方法,将当前元素累加到总和,最后耗尽迭代器,并返回总和

1
2
3
4
5
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let total: i32 = numbers.iter().sum();
println!("total: {}", total);
}

和迭代器适配器一样,Rust中还有min()、max()、nth()、last()、product()、fold()等消耗型适配器,这里也不展开了