[Pre-RFC] Iter advance


#1

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.


#2

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.


#3

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


#4

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

Sebastian Malton


#5

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


#6

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


#7

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).


#8

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

Sebastian Malton


#9

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.


#10

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)
})

#11

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.


#12

How is using for_each inefficient?


#13

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.


#14

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


#15

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: |_| ()


#16

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.