[Pre-RFC] Iter advance

I have sometimes needed to skip the next n items in an iterator but there doesn’t seem to be able a way to do that besides calling .next() n times which seems really slow.

So I propose the following function:

fn advance_by(&mut self, n: usize) -> Self {
    ...
}

This function would skip over the next n items in the iterator. I haven’t look at the code so see if it is possible to do this without having to internally call next() but to make this useful I would say that it shouldn’t if at all possible.

You can do this with nth(). By default that does just step through many calls to next(), but many iterator types implement it with something more optimized.

2 Likes

If I understood correctly, iter.skip(n).next() should do this.

That won’t work because skip takes self not &mut self

Sebastian Malton

iter.by_ref().skip(n).next()

There’s also Itertools::dropping() which is like a non-lazy skip().

I’m not sure I see the problem; it takes ownership of the original iter and returns a wrapped version that does the skipping when first invoked (ie remains lazy).

1 Like

If I am in a function where the iter is borrowed mutabily then I cannot use any function that takes ownership.

Sebastian Malton

As @CAD97 already pointed out, &mut impl Iterator: Iterator, so you can use a function that takes ownership by passing ownership of the mutable reference.

Yep. See this example:

let mut numbers = (0 .. 10).into_iter();
(&mut numbers).take(3).for_each(|x| {
  println!("{} is in the podium!", x);
});
numbers.for_each(|x| {
  println!("{} is not.", x)
})

Yes but that requires the for_each() which means that it is not efficient.

CAD97’s iter.by_ref().skip(n).next() is equivalent to cuviper’s iter.nth(n) however the next next() call will return the n+2’nd item which is a bit counter intuitive.

How is using for_each inefficient?

1 Like

It is not required at all, that was just my own coding style (when there is no break/early return); you can perfectly use for x in (&mut iterator).take(3) { /* ... */ }.

And, by the way, it.for_each(|x| { is easier for the compiler to optimize than for x in it {, which means that it is actually the one that may be more efficient.

Yes I understand, but my use case is to just discard the skipped elements entirely. So it is more efficient to just advance through if possible then calling next a bunch

Sebastian Malton

Skipping (the) n (first) elements of an iterator

  • lazily

    let mut iterator = iterator.skip(n);

  • eagerly

    (&mut iterator).take(n).for_each(std::mem::drop);

Aside: if ::std::mem::drop makes your code too readable, you can always use its “implementation” instead: |_| ()

1 Like

for_each can be faster than a plain for loop, because it allows internal iteration. Rather than repeating calls to next() that have to check state, some iterators can implement tighter inner loops. I had some benchmarks with chain() in PR42782:

But I would still bet on nth() if all you’re going to do is drop it.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.