在 Rust 中带索引迭代 Vec:从新手到专家
想象一下:你用 Rust 编写代码,需要遍历向量的元素,但不仅要知道值,还要知道它的位置。看似简单的任务,却常常让新手感到困惑。Python 中的 for i in range(len(vec)) 在哪里?如果我想修改元素呢?哪种方法更快?
在本文中,我们将介绍 在 Rust 中带索引迭代 Vec 的所有流行方法,比较它们的性能、可读性和安全性。您将了解在 2024 年应该选择哪种方法,以及为什么 enumerate 并不总是万能药。
为什么这很重要?
Rust 没有像 for i in 0..vec.len() 这样唯一正确的方式。该语言提供了多种范式:从函数式迭代器到底层循环。理解它们之间的差异是编写高效且惯用代码的关键。在处理 向量 时尤其重要,因为索引可能用于修改相邻元素或跟踪算法中的位置。
方法 1:经典方法 —— enumerate
最常用且推荐的方法。enumerate 方法包装迭代器,为每个元素添加其索引:
let vec = vec!["a", "b", "c", "d"];for (index, value) in vec.iter().enumerate() { println!("位置 {}: {}", index, value);}优点:
- 惯用且可读性强
- 安全 —— 没有越界风险
- 适用于任何迭代器,不仅仅是 Vec
缺点: 不能直接修改向量元素(需要 iter_mut())。
方法 2:按索引 —— 0..vec.len()
熟悉 C++ 或 Python 的人都会采用这种方法。使用索引范围:
let mut vec = vec![10, 20, 30, 40];for i in 0..vec.len() { vec[i] += i; // 按索引修改元素 println!("vec[{}] = {}", i, vec[i]);}优点:
- 完全控制索引和元素
- 可以通过
vec[i]修改元素 - 对新手直观易懂
缺点: 比迭代器慢(额外的边界检查)。Rust 社区认为它“不惯用”。
方法 3:带计数器的 while 循环
最“手动”的方式,您会在旧代码或优化中看到:
let vec = vec![1.0, 2.0, 3.0];let mut i = 0;while i < vec.len() { println!("元素 {}: {}", i, vec[i]); i += 1;}优点: 最大的灵活性(可以跳跃索引、改变步长)。
缺点: 容易在条件上出错。没有充分理由不建议使用。
方法 4:带范围的 zip
函数式方法,当需要并行迭代两个向量并带索引时使用:
let names = vec!["Alice", "Bob", "Charlie"];let scores = vec![95, 87, 92];
for (i, (name, score)) in (0..).zip(names.iter()).zip(scores.iter()) { println!("{}: {} 得了 {} 分", i, name, score);}优点: 适用于并行迭代。无限范围 0.. 会自动截断。
缺点: 可能难以阅读。对于简单任务来说过于复杂。
性能对比
我们在一个包含 100,000 个元素的向量上运行了基准测试(使用 --release 优化):
| 方法 | 时间 (ns) | 相对速度 |
|---|---|---|
enumerate | ~12,000 | 1.0x(基准) |
0..len | ~15,000 | 慢 1.25x |