问题
In C, a for loop has an optional increment section which I sometimes miss in Rust:
for (uint i = 0; i < max; i = step_function(i, j, k)) {
/* many lines of code! */
}
This could be written in Rust as:
let mut i: u32 = 0;
while (i < max) {
//
// many lines of code!
//
i = step_function(i, j, k);
}
... however this will introduce bugs if continue
exists somewhere in the "many lines of code". My personal preference is also to keep the increment at the top of the loop.
Without creating a special iterator to handle this, is there a way to loop that matches C style more closely, accounting for both issues mentioned?
By "special iterator", I mean not having to define an iterator type and methods outside the for loop.
While it may seem like an artificial requirement, having to define an iterator for a single use - adds some overhead both in reading and writing the code.
Although @kennytm's answer shows how a reusable StepByFn
iterator could work, using closures adds some constraints to the code that wouldn't exist otherwise.
回答1:
If you could import an external crate, you should use 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
}
}
And if you are really missing the C-style for loop, you could use the 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
}}
}
If you are restricting to using the standard library only, creating a special iterator would be the most idiomatic way.
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)
}
}
It is also possible to create an inline iterator with repeat().scan()
, but it is very ugly and does not express the intention very well
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
}
}
来源:https://stackoverflow.com/questions/43823042/is-it-possible-to-step-by-a-different-amount-each-iteration-without-creating-a-s