-   Notifications  You must be signed in to change notification settings 
- Fork 13.9k
Description
The behaviour of <Range<_> as Iterator>::nth has a slight mismatch with StepBy (or Step depending on your viewpoint) as @scottmcm has found out, resulting in sub-optimal performance.
 On every iteration, the range has to first step forwards n-1 times to get the next element and then advance again by 1.
I'm hoping we can improve step_by into a 100% zero-cost abstraction.
It seems like the performance issue is specific to StepBy<Range>. I'm thinking therefore that we could specialize Iterator for StepBy<Range<I>> such that it would use @scottmcm's suggested semantics. Like this:
impl<I> Iterator for StepBy<Range<I>> where I: Step { fn next(&mut self) -> Option<Self::Item> { self.first = false; if let Some(mut n) = self.start.add_usize(self.step+1) { if n < self.end { std::mem::swap(&mut self.start, &mut n); return Some(n); } } self.start = self.end.clone(); None } }That also avoids the branch on a regular next(). I haven't looked at the other methods but that boolean in StepBy could possibly become superfluous. During construction of the StepBy adapter, the size in .step_by(size) is decremented and this specialization has to counter-add 1 every time but that should be optimized away if inlined.
If someone were to depend on side-effects in Step::add_usize (when the trait is stabilized), this pre-stepping would become weird. Same thing with a hypothetical next_and_skip_ahead().
@scottmcm what do you think of this?