Applying `take()` on an infinite iterator should make it exactsizediterator

Rust Playground link

But…take doesn’t know the base iterator is infinite. (That is, we don’t have a trait for that.)


There's a couple of places where it'd be nice to have one, though.

For example, an InfiniteIterator::next_infinite -> Self::Item wouldn't need the Option wrapper, and we could always offer converting an infinite iterator into an array because it resolves how to handle the "error" cases that happen for finite iterators.

See ACP: Uplift `iter::repeat_n` from itertools · Issue #120 · rust-lang/libs-team · GitHub that also talked (in alternatives) about the possibility of adding an infinite iterator trait that would let Take be ESI here.

Or here's a previous conversation about how zipping with an infinite iterator would also be nice


Hang on, what is an "infinite iterator"? One that guarantees that next will never return None? Because of course you can keep calling next on every iterator and, unless it's a FusedIterator, it might eventually return Some again.

There's probably a clearer name to be bikeshed for this than "infinite".

1 Like

Yes, that's exactly what it means. Things like repeat(0) or (0..).into_iter() or repeat_with(rand), which will never return None and thus should have size_hint of (usize::MAX, None).

(Note that repeat(0).map(|_| panic!()) is still infinite, because it can't return None. And that's important for getting derived size_hints right, as in RangeFrom should have an infinite size_hint by scottmcm · Pull Request #42315 · rust-lang/rust · GitHub )


If we ever get pattern types or similar, and trait method refinement in subtraits, then we could perhaps have InfiniteIterator::next(&mut self) -> Option<Self::Item is Some> :yum:

(even without subtrait refinement, if we get refined impls, all infinite iterator types could still opt-in to the refined return type)


That would be nice, but it might not be quite enough. Part of the problem I encountered in Improve the `array::map` codegen by scottmcm · Pull Request #107634 · rust-lang/rust · GitHub was that Ok(foo).unwrap() didn't completely optimize out when it's cross-function and a large foo (like a long array of u32) because the Result (or, here, Option) ends up bigger than the inner value and thus there's some memcpying that doesn't get removed. (See also issues like Using ManuallyDrop causes allocas and memcpys that LLVM cannot remove · Issue #79914 · rust-lang/rust · GitHub)

So, unfortunately, for now at least there's still value in things that don't wrap stuff up into enums, like why exists inside libcore.