5

In Rust, how does one do variable sized steps in a for style loop? I'm able to do fixed sized steps with this construct:

for i in (0..vals.len()).step_by(4)
{
    println!("{}: {}", i, vals[i]);
}

or the more proper:

for (i,val) in vals.iter().enumerate().step_by(4)
{
    println!("{}: {}", i, val);
}

but what I'd really like to do is something like:

for i in 0..vals.len()
{
    println!("{}: {}", i, vals[i]);
    if      vals[i] == 1 { i += 2; }
    else if vals[i] == 2 { i += 4; }
}

but of course, modifying i doesn't affect the loop iterator.

Coming from a primarily C background, the reliance on iterators in modern languages often feels like programming with mitts on. Usually Google comes to the rescue, but I haven't found any solution to what seems like a fairly simple problem.

The best I've come up with is

let mut i:usize = 0;
while i < vals.len()
{
    println!("{}: {}", i, vals[i]);
    if      vals[i] == 1  { i += 2; }
    else if vals[i] == 2  { i += 4; }
}

but that feels like a poor man's for loop and contrary to what I should be doing to take advantage of an iterator-equipped language. Is there a variant of continue that skips iterations, or a way to call skip on the iterator from within the loop?

trent
  • 25,033
  • 7
  • 51
  • 90
Heath Raftery
  • 3,643
  • 17
  • 34
  • I don't actually think this is a duplicate, because the other question's iterator is a "true" iterator -- there's no way to skip 2 items except by calling `next()` twice. This case is narrower, and Lukas's answer is a good answer for this question, but does not apply to the other one. – trent Dec 14 '19 at 13:44

1 Answers1

5

There aren't really any better options. I could still think of this:

let mut skip = 0;
for (i, val) in vals.iter().enumerate() {
    if skip > 0 {
        skip -= 1;
        continue;
    }

    println!("{}: {}", i, val);

    skip = match val {
        1 => 2,
        2 => 4,
        _ => 0,
    };
}

But that's not necessarily better than the ideas you already had.

The reason there is no "nice" solution for that is simply because this use case is very rare. for loops are for the common case of completely iterating through an iterator. For situations where you need to have some more complex "advance iterator" logic, while and loop loops are there for you. Those loops are not "bad", they are exactly for these kinds of situations.

I think your while loop solution is perfectly fine!

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • 1
    Keeping a mutable skip counter outside the loop scope was my next ugliest solution too! Really appreciate this answer thanks to "`for` loops are for the common case..." - helped adjust my expectations of a `for` loop, and consider it intwined with the (limited and powerful) world of iterators. Performing functional computer science tasks is the least common case for me and side effects are usually my goal, so this has helped tune my aim. – Heath Raftery Dec 15 '19 at 07:36