如何在 Rust 中以步长 3、5 或 100 遍历数字?全面解析
想象一下:你用 Rust 编程,需要输出 0 到 20 之间的所有偶数。或者,构建一个序列 0, 5, 10, 15, 20。在 Python 中,你只需写 range(0, 21, 5) 就搞定了。那么在 Rust 中该怎么做呢?这里有一些细微之处,今天我们就来一一解析。
这个问题是 Stack Overflow 上最热门的问题之一(有数千次浏览)。这并不奇怪:标准的 for i in 0..10 只能以步长 1 工作。但如果需要其他步长呢?事实证明,总共有三种方法,每种方法都有其优点和陷阱。
我们不仅会展示代码——还会从性能、可读性和灵活性方面比较这些方法。开始吧!
为什么 Rust 没有内置带步长的 range?
简短的回答是:因为 Rust 中的 Range 是一个简单的迭代器,它生成一个整数序列。为了保持尽可能轻量和可预测,它默认不支持步长。但开发者们已经提供了解决这个问题的方法。
详细的回答是:在 Rust 生态系统中,哲学是“显式优于隐式”。创建者没有将步长塞入范围语法中,而是提供了灵活的迭代器适配工具。这提供了比看起来更多的可能性。
方法 1:step_by() —— 最直接的方式
Rust 标准库中有一个 step_by(n) 方法,它创建一个跳过每 n-1 个元素的迭代器。它的工作原理如下:
fn main() { // 输出从 0 到 20 步长为 5 的数字 for i in (0..=20).step_by(5) { println!("{}", i); } // 输出:0, 5, 10, 15, 20}请注意:我使用了 0..=20(包含),而不是 0..20(不包含)。如果需要步长为 3 直到 10,写 (0..10).step_by(3) —— 将得到 0, 3, 6, 9。
step_by() 的优点:
- 简单 —— 一行代码,无需闭包
- 可读性 —— 一眼就能看出在做什么
- 性能 —— 开销最小
缺点:
- 仅适用于整数(i32, u64 等)
- 无法在每次迭代中设置动态步长
- 仅在 Rust 1.28 中引入(但这已经很久了)
这个方法对于 90% 的任务来说是理想选择。如果你只需要以固定步长遍历数字——请使用 step_by()。
方法 2:filter() —— 当步长是一个条件时
如果步长不是固定的呢?例如,需要输出能被 3 整除的数字。或者数字各位之和大于 5 的数字。这时 filter() 就派上用场了:
fn main() { // 0 到 30 之间所有 4 的倍数 for i in (0..=30).filter(|x| x % 4 == 0) { println!("{}", i); } // 输出:0, 4, 8, 12, 16, 20, 24, 28
// 现在——所有不能被 3 整除的数字 for i in (0..20).filter(|x| x % 3 != 0) { println!("{}", i); } // 输出:1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19}这里我们传递一个闭包,它检查每个数字。filter() 只保留条件为真的元素。
何时 filter() 优于 step_by():
- 步长取决于值(例如,“所有质数”)
- 需要与其他条件组合
- 步长不固定(例如,斐波那契数列)
缺点:
- 比 step_by() 稍慢——必须检查每个数字
- 对于初学者来说,代码可能不太直观
我个人在需要的不只是“每隔 n 个”,而是“每个符合规则的元素”时使用 filter()。它更强大,但需要更多注意