在c中,一个for loop具有一个可选的增量部分,我有时会在Rust中错过:
for (uint i = 0; i < max; i = step_function(i, j, k)) {
/* many lines of code! */
}
这可以用生锈写为:
let mut i: u32 = 0;
while (i < max) {
//
// many lines of code!
//
i = step_function(i, j, k);
}
...但是,如果continue
存在于"许多代码行" 中的某个地方,这将引入错误。我的个人偏爱也是将增量保持在循环的顶部。
没有创建特殊的迭代器来处理此操作,是否有一种方法可以更紧密地匹配C样式?
由"特殊迭代器",我的意思是不必定义迭代仪类型和for循环外的方法。
虽然它似乎是人为的要求,但必须定义一个用于单一用途的迭代器 - 在阅读和编写代码时添加了一些开销。
尽管 @kennytm的答案显示了可重复使用的StepByFn
迭代器如何工作,但使用闭合添加了对代码的一些约束。
如果您可以导入外部板条箱,则应使用itertools::iterate
:
extern crate itertools;
use itertools::iterate;
fn main() {
for i in iterate(0, |i| 2*i + 3).take_while(|i| *i < 100) {
println!("{}", i);
// 0 3 9 21 45 93
}
}
,如果您是真的缺少循环的C风格,则可以使用cfor
板条箱:
#[macro_use] extern crate cfor;
fn main() {
cfor!{ let mut i = 0; i < 100; i = 2*i + 3; {
println!("{}", i);
// 0 3 9 21 45 93
}}
}
如果您仅限于使用标准库,则创建特殊的迭代器将是最惯用的方式。
fn main() {
for i in StepByFn::new(0, 100, |i| 2*i + 3) {
println!("{}", i);
// 0 3 9 21 45 93
}
}
struct StepByFn<T, F> {
begin: T,
end: T,
step: F,
}
impl<T, F: FnMut(&T) -> T> StepByFn<T, F> {
pub fn new(begin: T, end: T, step: F) -> StepByFn<T, F> {
StepByFn { begin, end, step }
}
}
impl<T: PartialOrd, F: FnMut(&T) -> T> Iterator for StepByFn<T, F> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.begin >= self.end {
return None;
}
let next = (self.step)(&self.begin);
let prev = std::mem::replace(&mut self.begin, next);
Some(prev)
}
}
也可以使用repeat().scan()
创建一个内联迭代器,但它非常丑陋,并且不太表达意图
use std::iter::repeat;
fn main() {
for i in repeat(()).scan(0, |i, ()| {
let old = *i;
*i = 2*old + 3;
if old < 100 { Some(old) } else { None }
}) {
println!("{}", i);
// 0 3 9 21 45 93
}
}