在C中,for循环有一个可选的增量部分,我有时会在Rust中错过:
for (uint i = 0; i < max; i = step_function(i, j, k)) {
/* many lines of code! */
}
这可以用Rust编写:
let mut i: u32 = 0;
while (i < max) {
//
// many lines of code!
//
i = step_function(i, j, k);
}
...但是如果{em>&#34;多行代码&#34; 中存在continue
,这将引入错误。我个人的偏好也是将增量保持在循环的顶部。
如果没有创建一个特殊的迭代器来处理这个问题,是否有一种方法可以更紧密地匹配C样式,同时解决上述两个问题?
通过&#34;特殊迭代器&#34;,我的意思是不必在for循环之外定义迭代器类型和方法。
虽然看起来像是一个人为的要求,但必须为一次使用定义一个迭代器 - 在读取和编写代码时会增加一些开销。
尽管@ kennytm的答案显示了可重用的StepByFn
迭代器如何工作,但使用闭包会为代码增加一些限制,否则就不会存在。
答案 0 :(得分:4)
如果您可以导入外部包,则应使用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-style for循环,你可以使用cfor
crate:
#[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
}
}