Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a stable way to iterate on a range with custom step?

Tags:

iterator

rust

How should I go if I want to iterate with a custom step in stable Rust? Essentially something like the C/C++

for (int i = 0; i < n; i += 2) {

}

I've already tried using range_step_inclusive and the solutions in How do I iterate over a range with a custom step?:

use std::iter::range_step_inclusive;
for i in range_step_inclusive(0, n, 2) {
    println!("i: {}", i);
}

But it seems like it isn't available in Rust 1.1:

error: unresolved import `std::iter::range_step_inclusive`. There is no `range_step_inclusive` in `std::iter`

What would be an alternative? Possibly the idiomatic way for creating custom ranges.

like image 293
Sosdoc Avatar asked Jul 12 '15 19:07

Sosdoc


3 Answers

Rust 1.28+

Iterator::step_by is now stable:

fn main() {
    for i in (0..100).step_by(2) {
        println!("{}", i);
    }
}

Rust 1.1+

You can always write it out the old-fashioned way:

fn main() {
    let mut i = 0;
    while i < 100 {
        println!("i: {}", i);
        i += 2;
    }
}

Which can then be abstracted:

use std::ops::Add;

fn step_by<T, F>(start: T, end_exclusive: T, step: T, mut body: F)
where
    T: Add<Output = T> + PartialOrd + Copy,
    F: FnMut(T),
{
    let mut i = start;
    while i < end_exclusive {
        body(i);
        i = i + step;
    }
}

fn main() {
    step_by(0, 100, 2, |i| {
        println!("i: {}", i);
    })
}

Interesting historical side note, I believe that originally all the looping was done with closures like this, before iterators became extremely prevalent.

You can then take this and make it into an iterator:

use std::ops::Add;

struct StepBy<T> {
    start: T,
    end_exclusive: T,
    step: T,
}

impl<T> StepBy<T> {
    fn new(start: T, end_exclusive: T, step: T) -> Self {
        Self {
            start,
            end_exclusive,
            step,
        }
    }
}

impl<T> Iterator for StepBy<T>
where
    T: Add<Output = T> + PartialOrd + Copy,
{
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        if self.start < self.end_exclusive {
            let v = self.start;
            self.start = self.start + self.step;
            Some(v)
        } else {
            None
        }
    }
}

fn main() {
    for i in StepBy::new(0, 100, 2) {
        println!("i: {}", i);
    }
}

See also:

  • How can I add new methods to Iterator?
like image 181
Shepmaster Avatar answered Oct 10 '22 04:10

Shepmaster


There is way using let "redefinition":

for i in 0..((n + 1) / 2) {
    let i = i * 2;
    // …
}

Or use Iterator::map:

for i in (0..((n + 1) / 2)).map(|i| i * 2) {
    // …
}
like image 30
Hauleth Avatar answered Oct 10 '22 03:10

Hauleth


I think i'll stick to a while loop. But if you really want an iterator based method you could try this

fn main(){
    let (start, step, end) = (1, 2, 20);
    for i in (0..).map(|x| start+step*x)
                  .take_while(|&x| x<end){
        println!("{:?}", i);
    }
}
like image 34
basic_bgnr Avatar answered Oct 10 '22 02:10

basic_bgnr