std::collections
Rust 标准库中提供的数据容器都在 std::collections 模块中, 常用的容器及适用场景如下:
| 类型 | 作用 | 适用场景 |
|---|---|---|
| Vec | 动态数组 | 需要按下标随机访问, 尾部增删 |
| VecDeque | 双端队列 | 需要频繁在两端插入/删除(队列) |
| HashMap | 键值对(无序) | 需要根据键快速查找值 |
| HashSet | 不重复集合(无序) | 去重/判断元素是否存在 |
| LinkedList | 双向链表 | 很少使用, 绝大多数场景用 Vec 代替 |
| BTreeMap | 键值对(有序) | 需要按键的顺序遍历 |
| BTreeSet | 不重复集合(有序) | 需要有序且不重复 |
| BinaryHeap | 优先队列(最大堆) | 需要每次取出最大/最小元素 |
注意点
Vec 虽然不定义在 std::collections 模块中, 但是由于它太常用了, 被放进了 prelude 里, 不需要 use 就能直接使用; 其他容器都需要先 use std::collections::xxx
Vec
Vec 是最常用的数据容器, 是一个动态数组, 长度可以在运行时改变, 元素存储在连续的内存中, 按下标访问的速度是 O(1)
创建 Vec
| 方式 | 作用 |
|---|---|
| vec! 宏 | 创建并初始化 Vec(最常用) |
| Vec::new | 创建空的 Vec |
| Vec::with_capacity | 创建指定容量的空 Vec(提前分配内存, 避免多次扩容) |
| Vec::from | 从其他类型(如数组)转换 |
fn main() {
// 1. 使用 vec! 宏创建并初始化(最常用)
let v1 = vec![1, 2, 3];
println!("{v1:?}"); // [1, 2, 3]
// 2. 创建指定数量的相同元素: 5 个 0
let v2 = vec![0; 5];
println!("{v2:?}"); // [0, 0, 0, 0, 0]
// 3. 创建空的 Vec(需要指定元素类型)
let v3: Vec<i32> = Vec::new();
println!("{v3:?}"); // []
// 4. 指定容量创建: len 还是 0, 但是内存已经分配好了
let v4: Vec<i32> = Vec::with_capacity(10);
println!("len = {}, capacity = {}", v4.len(), v4.capacity()); // len = 0, capacity = 10
// 5. 从数组转换
let v5 = Vec::from([1, 2, 3]);
println!("{v5:?}"); // [1, 2, 3]
}增删改查
| 方法 | 作用 |
|---|---|
| push | 在末尾添加一个元素 |
| pop | 删除并返回末尾元素, 返回 Option |
| insert | 在指定位置插入元素(后面的元素会后移) |
| remove | 删除并返回指定位置的元素(后面的元素会前移) |
| append | 将另一个 Vec 的所有元素追加到末尾(另一个 Vec 变为空) |
| extend | 将迭代器中的所有元素追加到末尾 |
| clear | 清空所有元素 |
fn main() {
let mut nums = vec![1, 2, 3];
// push: 在末尾添加元素
nums.push(4);
println!("{nums:?}"); // [1, 2, 3, 4]
// pop: 删除并返回末尾元素, 返回 Option(空数组返回 None)
let last = nums.pop();
println!("last = {last:?}, nums = {nums:?}"); // last = Some(4), nums = [1, 2, 3]
// insert: 在指定位置插入元素
nums.insert(1, 99);
println!("{nums:?}"); // [1, 99, 2, 3]
// remove: 删除并返回指定位置的元素
let removed = nums.remove(1);
println!("removed = {removed}, nums = {nums:?}"); // removed = 99, nums = [1, 2, 3]
// append: 追加另一个 Vec 的所有元素, 另一个 Vec 变为空
let mut nums2 = vec![4, 5];
nums.append(&mut nums2);
println!("nums = {nums:?}, nums2 = {nums2:?}"); // nums = [1, 2, 3, 4, 5], nums2 = []
// extend: 追加迭代器中的元素
nums.extend([6, 7]);
println!("{nums:?}"); // [1, 2, 3, 4, 5, 6, 7]
// clear: 清空所有元素
nums.clear();
println!("{nums:?}"); // []
}访问元素
| 方式 | 作用 |
|---|---|
nums[i] 索引 | 直接访问, 越界会 panic |
| get | 返回 Option, 越界返回 None, 不会 panic |
| get_mut | 获取元素的可变引用 |
| first / last | 第一个/最后一个元素 |
fn main() {
let nums = vec![10, 20, 30];
// 1. 使用索引直接访问, 越界会 panic
println!("nums[0] = {}", nums[0]); // nums[0] = 10
// 2. 使用 get 访问, 越界返回 None, 不会 panic
println!("{:?}", nums.get(1)); // Some(20)
println!("{:?}", nums.get(99)); // None
// 3. first / last: 获取第一个/最后一个元素
println!("{:?}", nums.first()); // Some(10)
println!("{:?}", nums.last()); // Some(30)
// 4. get_mut: 获取可变引用, 修改元素
let mut nums = vec![10, 20, 30];
if let Some(x) = nums.get_mut(0) {
*x = 100;
}
println!("{nums:?}"); // [100, 20, 30]
}注意点
使用索引 nums[i] 访问元素时, 如果索引越界会直接 panic, 不确定索引是否有效时, 优先使用 get()
遍历 Vec
| 方式 | 说明 |
|---|---|
for n in nums | 直接遍历, 转移所有权, 遍历后 nums 不可用 |
for n in &nums | 借用遍历, 遍历后 nums 还可以使用 |
for n in &mut nums | 可变借用遍历, 可以修改元素 |
fn main() {
let nums = vec![1, 2, 3];
// 1. 直接遍历(等价于 into_iter, 会转移所有权)
for n in nums {
println!("{n}"); // n 的类型是 i32
}
// 注意: 遍历后 nums 已经被消耗, 不能再使用
// 2. 使用引用遍历(不转移所有权)
let nums = vec![1, 2, 3];
for n in &nums {
println!("{n}"); // n 的类型是 &i32
}
println!("nums = {nums:?}"); // 遍历后还可以使用
// 3. 使用可变引用遍历(可以修改元素)
let mut nums = vec![1, 2, 3];
for n in &mut nums {
*n *= 10;
}
println!("{nums:?}"); // [10, 20, 30]
// 4. 带索引遍历
let nums = vec![10, 20, 30];
for (i, n) in nums.iter().enumerate() {
println!("nums[{i}] = {n}");
}
}排序
| 方法 | 作用 |
|---|---|
| sort | 稳定排序, 相等的元素保持原来的相对顺序 |
| sort_unstable | 不稳定排序, 更快, 大多数场景推荐使用 |
| sort_by | 自定义比较规则排序 |
| sort_unstable_by | 自定义比较规则的不稳定排序 |
fn main() {
// sort_unstable: 默认升序排序
let mut nums = vec![3, 1, 2];
nums.sort_unstable();
println!("{nums:?}"); // [1, 2, 3]
// sort_unstable_by: 自定义比较规则, 实现降序
let mut nums = vec![1, 2, 3, 4];
nums.sort_unstable_by(|a, b| b.cmp(a));
println!("{nums:?}"); // [4, 3, 2, 1]
}注意点
如果只需要排序, 不需要保证"相等元素的相对顺序", 推荐使用 sort_unstable, 性能更好
其他常用方法
| 方法 | 作用 |
|---|---|
| len / is_empty | 长度/是否为空 |
| contains | 是否包含某个元素 |
| dedup | 去除相邻重复的元素(需要先排序) |
| reverse | 反转所有元素 |
| retain | 只保留满足条件的元素 |
| resize | 调整长度, 变长时用默认值填充 |
fn main() {
let mut nums = vec![3, 1, 2, 1];
// len / is_empty
println!("len = {}, is_empty = {}", nums.len(), nums.is_empty()); // len = 4, is_empty = false
// contains: 是否包含某个元素
println!("contains 1 = {}", nums.contains(&1)); // contains 1 = true
// sort + dedup: 先排序再去除相邻重复的元素
nums.sort();
nums.dedup();
println!("{nums:?}"); // [1, 2, 3]
// reverse: 反转
nums.reverse();
println!("{nums:?}"); // [3, 2, 1]
// retain: 只保留满足条件的元素(这里是保留偶数)
let mut nums = vec![1, 2, 3, 4, 5];
nums.retain(|n| *n % 2 == 0);
println!("{nums:?}"); // [2, 4]
// resize: 调整长度, 变长部分用默认值(0)填充
let mut nums = vec![1, 2, 3];
nums.resize(5, 0);
println!("{nums:?}"); // [1, 2, 3, 0, 0]
}与切片互相转换
fn main() {
let nums = vec![1, 2, 3];
// 1. Vec -> 切片: 使用 &nums[..] 或者 &nums
let slice: &[i32] = &nums[..];
println!("{slice:?}"); // [1, 2, 3]
// 2. 切片 -> Vec: 使用 to_vec()
let tail: Vec<i32> = nums[1..].to_vec();
println!("{tail:?}"); // [2, 3]
// 3. 函数参数一般使用切片, 这样 Vec 和数组都能传入
print_sum(&nums); // sum = 6
print_sum(&[4, 5, 6]); // sum = 15
}
fn print_sum(nums: &[i32]) {
let sum: i32 = nums.iter().sum();
println!("sum = {sum}");
}VecDeque
VecDeque 是双端队列, 可以从两端添加/删除元素, 底层是环形缓冲区, 两端的操作都是 O(1)
使用场景: 需要实现队列(先进先出)时, 或者需要频繁在两端插入/删除时
创建 VecDeque
fn main() {
use std::collections::VecDeque;
// 1. 创建空的 VecDeque
let mut q: VecDeque<i32> = VecDeque::new();
println!("{q:?}"); // []
// 2. 从 Vec 转换
let q = VecDeque::from(vec![1, 2, 3]);
println!("{q:?}"); // [1, 2, 3]
// 3. 使用 collect 从迭代器创建
let q: VecDeque<i32> = (1..=3).collect();
println!("{q:?}"); // [1, 2, 3]
}两端操作
| 方法 | 作用 |
|---|---|
| push_back / push_front | 在末尾/开头添加元素 |
| pop_back / pop_front | 从末尾/开头删除并返回元素 |
| front / back | 查看两端的元素(返回 Option) |
| get | 按下标获取元素(返回 Option) |
fn main() {
use std::collections::VecDeque;
let mut q: VecDeque<i32> = VecDeque::new();
// push_back: 在末尾添加(队列的入队操作)
q.push_back(1);
q.push_back(2);
println!("{q:?}"); // [1, 2]
// push_front: 在开头添加
q.push_front(0);
println!("{q:?}"); // [0, 1, 2]
// pop_front: 从开头删除(队列的出队操作)
let x = q.pop_front();
println!("x = {x:?}, q = {q:?}"); // x = Some(0), q = [1, 2]
// pop_back: 从末尾删除
let x = q.pop_back();
println!("x = {x:?}, q = {q:?}"); // x = Some(2), q = [1]
// front / back: 查看两端的元素, 不删除
let q = VecDeque::from(vec![1, 2, 3]);
println!("front = {:?}, back = {:?}", q.front(), q.back()); // front = Some(1), back = Some(3)
// get: 按下标获取元素
println!("{:?}", q.get(1)); // Some(2)
println!("{:?}", q.get(99)); // None
}注意点
VecDeque 不支持 q[i] 这种索引访问语法(没有实现 Index trait), 需要按下标访问时使用 get()
经典使用场景: 队列
fn main() {
use std::collections::VecDeque;
// 实现一个简单的任务队列: 先进先出
let mut queue: VecDeque<String> = VecDeque::new();
// 任务入队
queue.push_back(String::from("任务1"));
queue.push_back(String::from("任务2"));
queue.push_back(String::from("任务3"));
println!("队列长度: {}", queue.len()); // 队列长度: 3
// 任务出队(按顺序处理)
while let Some(task) = queue.pop_front() {
println!("处理: {task}");
}
println!("队列是否为空: {}", queue.is_empty()); // 队列是否为空: true
}HashMap
HashMap 是键值对容器, 底层是哈希表, 根据键查找值的平均时间复杂度是 O(1), 是除了 Vec 之外最常用的容器
创建 HashMap
| 方式 | 作用 |
|---|---|
| HashMap::new | 创建空的 HashMap |
| HashMap::from | 从键值对数组转换 |
| collect | 从迭代器收集(最灵活) |
fn main() {
use std::collections::HashMap;
// 1. 创建空的 HashMap
let mut map: HashMap<&str, i32> = HashMap::new();
println!("{map:?}"); // {}
// 2. 从键值对数组转换
let map = HashMap::from([("name", "张三"), ("age", "18")]);
println!("{map:?}"); // {"name": "张三", "age": "18"} (键值对的打印顺序不确定)
// 3. 从迭代器收集
let map: HashMap<&str, i32> = vec![("a", 1), ("b", 2)].into_iter().collect();
println!("{map:?}"); // {"a": 1, "b": 2} (打印顺序不确定)
}增删改查
| 方法 | 作用 |
|---|---|
| insert | 插入键值对, 返回被替换的旧值 |
| get | 根据键获取值, 返回 Option |
| get_mut | 获取值的可变引用 |
| remove | 根据键删除, 返回被删除的值 |
| contains_key | 是否包含某个键 |
| len / is_empty | 长度/是否为空 |
fn main() {
use std::collections::HashMap;
let mut map: HashMap<&str, i32> = HashMap::new();
// insert: 插入键值对, 返回被替换的旧值
// 第一次插入, 没有旧值, 返回 None
let old = map.insert("a", 1);
println!("old = {old:?}"); // old = None
// 键已存在, 覆盖旧值, 返回旧值 Some(1)
let old = map.insert("a", 100);
println!("old = {old:?}"); // old = Some(1)
// get: 根据键获取值, 键不存在返回 None
println!("{:?}", map.get("a")); // Some(100)
println!("{:?}", map.get("b")); // None
// contains_key: 判断键是否存在
println!("contains_key a = {}", map.contains_key("a")); // contains_key a = true
// get_mut: 获取可变引用, 修改值
if let Some(x) = map.get_mut("a") {
*x += 1;
}
println!("{:?}", map.get("a")); // Some(101)
// remove: 根据键删除, 返回被删除的值
let removed = map.remove("a");
println!("removed = {removed:?}, map = {map:?}"); // removed = Some(101), map = {}
}遍历 HashMap
fn main() {
use std::collections::HashMap;
let mut map: HashMap<&str, i32> = HashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// 1. 遍历键值对
for (k, v) in &map {
println!("{k}: {v}");
}
// 2. 只遍历键
for k in map.keys() {
println!("{k}");
}
// 3. 只遍历值
for v in map.values() {
println!("{v}");
}
// 4. 遍历并修改值(可变引用)
let mut map: HashMap<&str, i32> = HashMap::new();
map.insert("a", 1);
for v in map.values_mut() {
*v *= 10;
}
println!("{map:?}"); // {"a": 10}
}注意点
HashMap 的遍历顺序是不确定的, 如果代码逻辑依赖遍历顺序, 请使用 BTreeMap
entry API
entry 是操作 HashMap 最常用的 API, 它返回键对应的入口, 可以避免"先判断键是否存在, 再插入/修改"这种重复代码
| 方法 | 作用 |
|---|---|
| entry | 获取键对应的入口 |
| or_insert | 键不存在时插入默认值, 返回值的可变引用 |
| or_insert_with | 键不存在时通过闭包创建值(惰性求值) |
| or_default | 键不存在时插入类型的默认值 |
经典场景: 统计每个字符出现的次数
fn main() {
use std::collections::HashMap;
// 统计字符串中每个字符出现的次数
let mut counts: HashMap<char, i32> = HashMap::new();
for c in "hello world".chars() {
// entry(c): 获取字符 c 对应的入口
// or_insert(0): 如果键不存在, 就插入 0, 然后返回值的可变引用
// 如果键已存在, 直接返回已有值的可变引用
*counts.entry(c).or_insert(0) += 1;
}
println!("{counts:?}"); // 每个字符的出现次数
println!("l 出现了 {} 次", counts[&'l']); // l 出现了 3 次
}or_insert_with 的经典场景: 键不存在时才创建值(避免不必要的计算)
fn main() {
use std::collections::HashMap;
let mut map: HashMap<&str, String> = HashMap::new();
// 键不存在, 执行闭包创建默认值
let v = map.entry("a").or_insert_with(|| String::from("默认值"));
println!("v = {v}"); // v = 默认值
// 键已存在, 闭包不会执行, 直接返回原来的值
let v = map.entry("a").or_insert_with(|| String::from("新值"));
println!("v = {v}"); // v = 默认值
}HashMap 的键要求
HashMap 的键必须实现 Hash 和 Eq 两个 trait:
- 基本类型(
i32/&str/String/bool等)都默认实现了, 可以直接作为键 - 自定义结构体需要手动实现, 或者使用派生宏自动实现
// 使用派生宏自动实现 Hash 和 Eq trait
#[derive(Hash, Eq, PartialEq, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
use std::collections::HashMap;
let mut map: HashMap<Point, String> = HashMap::new();
map.insert(Point { x: 1, y: 2 }, String::from("第一个点"));
println!("{:?}", map.get(&Point { x: 1, y: 2 })); // Some("第一个点")
}HashSet
HashSet 是无序集合, 存储不重复的元素, 底层和 HashMap 一样是哈希表(只存键不存值), 判断元素是否存在的时间复杂度是 O(1)
创建与增删查
| 方法 | 作用 |
|---|---|
| HashSet::new | 创建空集合 |
| insert | 插入元素, 返回是否是新插入的 |
| contains | 是否包含某个元素 |
| remove | 删除元素, 返回是否删除成功 |
fn main() {
use std::collections::HashSet;
// 1. 创建空集合
let mut set: HashSet<i32> = HashSet::new();
// 2. 从数组转换
let set = HashSet::from([1, 2, 3]);
println!("{set:?}"); // {1, 2, 3} (打印顺序不确定)
// 3. insert: 返回是否是新插入的(重复插入返回 false)
let mut set: HashSet<i32> = HashSet::new();
println!("insert 1 = {}", set.insert(1)); // insert 1 = true
println!("insert 1 = {}", set.insert(1)); // insert 1 = false
println!("{set:?}"); // {1}
// 4. contains: 判断元素是否存在
println!("contains 1 = {}", set.contains(&1)); // contains 1 = true
// 5. remove: 删除元素
println!("remove 1 = {}", set.remove(&1)); // remove 1 = true
println!("{set:?}"); // {}
}经典使用场景: 去重
fn main() {
use std::collections::HashSet;
// 给 Vec 去重
let v = vec![1, 2, 3, 3, 2, 1];
let set: HashSet<i32> = v.into_iter().collect();
println!("{set:?}"); // {1, 2, 3} (打印顺序不确定)
}集合运算
| 方法 | 作用 |
|---|---|
| union | 并集: 两个集合中的所有元素 |
| intersection | 交集: 两个集合共有的元素 |
| difference | 差集: 在 A 中但不在 B 中的元素 |
| symmetric_difference | 对称差集: 只出现在其中一个集合中的元素 |
fn main() {
use std::collections::HashSet;
let a: HashSet<i32> = HashSet::from([1, 2, 3]);
let b: HashSet<i32> = HashSet::from([2, 3, 4]);
// 并集
let union: HashSet<_> = a.union(&b).collect();
println!("{union:?}"); // {1, 2, 3, 4} (打印顺序不确定)
// 交集
let intersection: HashSet<_> = a.intersection(&b).collect();
println!("{intersection:?}"); // {2, 3} (打印顺序不确定)
// 差集: 在 a 中但不在 b 中
let difference: HashSet<_> = a.difference(&b).collect();
println!("{difference:?}"); // {1} (打印顺序不确定)
// 对称差集: 只出现在其中一个集合中
let sym_diff: HashSet<_> = a.symmetric_difference(&b).collect();
println!("{sym_diff:?}"); // {1, 4} (打印顺序不确定)
}LinkedList
LinkedList 是双向链表, 每个元素都通过指针连接, 与 Vec 相比, 在中间位置插入/删除更快, 但是按下标访问很慢(需要从头遍历)
注意点
LinkedList 在实际开发中很少使用, 因为链表的元素在内存中不连续, 对 CPU 缓存不友好, 大多数场景下 Vec 或 VecDeque 的性能反而更好
fn main() {
use std::collections::LinkedList;
let mut list: LinkedList<i32> = LinkedList::new();
// push_back: 在末尾添加
list.push_back(1);
list.push_back(2);
// push_front: 在开头添加
list.push_front(0);
println!("{list:?}"); // [0, 1, 2]
// pop_front / pop_back: 从两端删除
println!("{:?}", list.pop_front()); // Some(0)
println!("{:?}", list.pop_back()); // Some(2)
println!("{list:?}"); // [1]
// 遍历(链表不支持索引访问)
for n in &list {
println!("{n}");
}
}了解
以下容器使用频率相对较低, 了解特点和适用场景即可, 具体用法可以查阅标准库文档
BTreeMap
BTreeMap 和 HashMap 一样是键值对容器, 区别是 BTreeMap 按键有序存储, 可以按照键的顺序遍历, 但是查找速度比 HashMap 慢(O(log n))
fn main() {
use std::collections::BTreeMap;
// 插入后会自动按照键排序
let mut map = BTreeMap::new();
map.insert("c", 3);
map.insert("a", 1);
map.insert("b", 2);
// 按照键的顺序遍历(字母序)
for (k, v) in &map {
println!("{k}: {v}"); // a: 1 / b: 2 / c: 3
}
}BTreeSet
BTreeSet 和 HashSet 一样存储不重复的元素, 区别是 BTreeSet 中的元素有序
fn main() {
use std::collections::BTreeSet;
// 自动去重并且有序
let set: BTreeSet<i32> = [3, 1, 2, 2, 1].into_iter().collect();
println!("{set:?}"); // {1, 2, 3}
}BinaryHeap
BinaryHeap 是优先队列, 默认是最大堆, 每次取出的都是当前最大的元素
fn main() {
use std::collections::BinaryHeap;
let mut heap = BinaryHeap::new();
heap.push(3);
heap.push(1);
heap.push(2);
// pop: 每次取出最大的元素
println!("{:?}", heap.pop()); // Some(3)
println!("{:?}", heap.pop()); // Some(2)
println!("{:?}", heap.pop()); // Some(1)
// peek: 查看最大的元素, 不删除
let heap = BinaryHeap::from(vec![1, 5, 3]);
println!("{:?}", heap.peek()); // Some(5)
// 如果需要最小堆, 使用 std::cmp::Reverse 反转比较顺序
let mut heap = BinaryHeap::new();
heap.push(std::cmp::Reverse(3));
heap.push(std::cmp::Reverse(1));
heap.push(std::cmp::Reverse(2));
println!("{:?}", heap.pop()); // Some(Reverse(1))
}迭代器 Iterator
迭代器是 Rust 中处理数据集合的核心抽象, 标准库中几乎所有的容器都实现了 Iterator trait, 通过迭代器可以对数据做过滤/转换/聚合等操作
三种迭代方式
| 方式 | 迭代的元素 | 所有权 |
|---|---|---|
iter() | 引用 &T | 借用, 迭代后原数据可用 |
iter_mut() | 可变引用 &mut T | 可变借用, 可以修改元素 |
into_iter() | 值本身 T | 转移所有权 |
fn main() {
let nums = vec![1, 2, 3];
// iter(): 迭代的是引用, 不转移所有权
let v: Vec<&i32> = nums.iter().collect();
println!("{v:?}"); // [1, 2, 3]
println!("nums = {nums:?}"); // 迭代后原数据还可以使用
// iter_mut(): 迭代的是可变引用, 可以修改元素
let mut nums = vec![1, 2, 3];
for n in nums.iter_mut() {
*n += 1;
}
println!("{nums:?}"); // [2, 3, 4]
// into_iter(): 迭代的是值本身, 会转移所有权
let nums = vec![1, 2, 3];
let v: Vec<i32> = nums.into_iter().collect();
println!("{v:?}"); // [1, 2, 3]
// 注意: nums 已经被消耗, 不能再使用
}迭代器适配器
这些方法接收迭代器, 返回新的迭代器, 是惰性求值的: 只有被消费时才会真正执行
| 方法 | 作用 |
|---|---|
| map | 对每个元素进行转换 |
| filter | 过滤元素(保留满足条件的) |
| filter_map | 过滤 + 转换(返回 None 的元素被丢弃) |
| enumerate | 带上索引, 生成 (索引, 元素) 元组 |
| zip | 将两个迭代器按位置配对(以短的为准) |
| chain | 连接两个迭代器 |
| take / skip | 取前 n 个 / 跳过前 n 个 |
fn main() {
let nums = vec![1, 2, 3, 4, 5];
// map: 每个元素转换
let x: Vec<i32> = nums.iter().map(|&n| n * 2).collect();
println!("{x:?}"); // [2, 4, 6, 8, 10]
// filter: 过滤(保留偶数)
let x: Vec<i32> = nums.iter().filter(|&&n| n % 2 == 0).copied().collect();
println!("{x:?}"); // [2, 4]
// filter_map: 过滤 + 转换(返回 None 的被丢弃)
let x: Vec<i32> = nums
.iter()
.filter_map(|&n| if n % 2 == 0 { Some(n * 10) } else { None })
.collect();
println!("{x:?}"); // [20, 40]
// enumerate: 带索引
for (i, n) in nums.iter().enumerate() {
println!("nums[{i}] = {n}");
}
// zip: 将两个迭代器配对(以短的为准)
let a = vec![1, 2, 3];
let b = vec![10, 20];
let x: Vec<(i32, i32)> = a.into_iter().zip(b).collect();
println!("{x:?}"); // [(1, 10), (2, 20)]
// chain: 连接两个迭代器
let x: Vec<i32> = vec![1, 2].into_iter().chain(vec![3, 4]).collect();
println!("{x:?}"); // [1, 2, 3, 4]
// take: 取前 n 个
let x: Vec<i32> = nums.iter().take(2).copied().collect();
println!("{x:?}"); // [1, 2]
// skip: 跳过前 n 个
let x: Vec<i32> = nums.iter().skip(2).copied().collect();
println!("{x:?}"); // [3, 4, 5]
}消费迭代器
这些方法会立即执行整个迭代过程, 得到一个最终结果
| 方法 | 作用 |
|---|---|
| collect | 将迭代器收集为容器(Vec/HashSet 等) |
| count | 元素个数 |
| sum | 求和 |
| fold | 自定义累加(可以指定起始值) |
| for_each | 对每个元素执行操作 |
fn main() {
let nums = vec![1, 2, 3, 4, 5];
// collect: 收集为其他容器
let v: Vec<i32> = nums.iter().copied().collect();
println!("{v:?}"); // [1, 2, 3, 4, 5]
let set: std::collections::HashSet<i32> = nums.iter().copied().collect();
println!("{set:?}"); // {1, 2, 3, 4, 5} (打印顺序不确定)
// count: 元素个数
println!("count = {}", nums.iter().count()); // count = 5
// sum: 求和(需要指定结果类型)
let total: i32 = nums.iter().sum();
println!("total = {total}"); // total = 15
// fold: 自定义累加, 第一个参数是起始值
let total = nums.iter().fold(0, |acc, &n| acc + n);
println!("total = {total}"); // total = 15
// fold 也可以用来求最大值
let max = nums.iter().fold(i32::MIN, |acc, &n| acc.max(n));
println!("max = {max}"); // max = 5
// for_each: 对每个元素执行操作
nums.iter().for_each(|n| println!("{n}"));
}迭代器链式调用
迭代器的强大之处在于可以链式组合, 一次遍历完成复杂的处理逻辑
fn main() {
let nums = vec![1, 2, 3, 4, 5, 6];
// 需求: 取出所有偶数 -> 每个乘以 10 -> 只保留前 2 个 -> 求和
let result: i32 = nums
.iter()
.filter(|&&n| n % 2 == 0) // 保留偶数 [2, 4, 6]
.map(|&n| n * 10) // 乘以 10 [20, 40, 60]
.take(2) // 只取前 2 个 [20, 40]
.sum(); // 求和
println!("result = {result}"); // result = 60
}范围 Range
范围 1..5 / 1..=5 本身就是一个迭代器, 可以配合迭代器方法使用
fn main() {
// 1..5: 不包含 5
for i in 1..5 {
println!("{i}"); // 1 2 3 4
}
// 1..=5: 包含 5
for i in 1..=5 {
println!("{i}"); // 1 2 3 4 5
}
// 与迭代器方法配合
let sum: i32 = (1..=100).sum();
println!("sum = {sum}"); // sum = 5050
let v: Vec<i32> = (0..5).map(|n| n * n).collect();
println!("{v:?}"); // [0, 1, 4, 9, 16]
// rev: 反转迭代方向
let v: Vec<i32> = (1..=3).rev().collect();
println!("{v:?}"); // [3, 2, 1]
}迭代器相关的开源库
- itertools: 扩展了大量迭代器方法, 如
combinations(组合)/permutations(排列)/unique(去重)等