Skip to content

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从其他类型(如数组)转换
rust
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清空所有元素
rust
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第一个/最后一个元素
rust
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可变借用遍历, 可以修改元素
rust
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自定义比较规则的不稳定排序
rust
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调整长度, 变长时用默认值填充
rust
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]
}

与切片互相转换

rust
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

rust
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)
rust
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()

经典使用场景: 队列

rust
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从迭代器收集(最灵活)
rust
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长度/是否为空
rust
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

rust
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键不存在时插入类型的默认值

经典场景: 统计每个字符出现的次数

rust
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 的经典场景: 键不存在时才创建值(避免不必要的计算)

rust
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 的键必须实现 HashEq 两个 trait:

  • 基本类型(i32/&str/String/bool 等)都默认实现了, 可以直接作为键
  • 自定义结构体需要手动实现, 或者使用派生宏自动实现
rust
// 使用派生宏自动实现 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删除元素, 返回是否删除成功
rust
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:?}"); // {}
}

经典使用场景: 去重

rust
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对称差集: 只出现在其中一个集合中的元素
rust
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 缓存不友好, 大多数场景下 VecVecDeque 的性能反而更好

rust
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

BTreeMapHashMap 一样是键值对容器, 区别是 BTreeMap 按键有序存储, 可以按照键的顺序遍历, 但是查找速度比 HashMap 慢(O(log n))

rust
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

BTreeSetHashSet 一样存储不重复的元素, 区别是 BTreeSet 中的元素有序

rust
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优先队列, 默认是最大堆, 每次取出的都是当前最大的元素

rust
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转移所有权
rust
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 个
rust
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对每个元素执行操作
rust
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}"));
}

迭代器链式调用

迭代器的强大之处在于可以链式组合, 一次遍历完成复杂的处理逻辑

rust
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 本身就是一个迭代器, 可以配合迭代器方法使用

rust
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(去重)等

Released under the MIT License.