Implement ExactSizeIterator for Zip<A, Repeat<B>> by 414owen · Pull Request #146642 · rust-lang/rust (original) (raw)

@414owen

Where:
A: ExactSizeIterator,
B: Clone,

And the symmetrical instance.

The only other instance for ExactSizeIterator<Zip<_, _>> requires each zipped iterator to be ExactSizeIterator, and since Repeat does not implement ExactSizeIterator, these instances don't overlap with anything.


I posted on the internals forum, and was told these instances would be insta-stable.

The stable attribute seems to require feature and since attributes, which I'm not entirely sure I've done correctly. Pointers are welcome.

@rustbot rustbot added S-waiting-on-review

Status: Awaiting review from the assignee but also interested parties.

T-libs

Relevant to the library team, which will review and decide on the PR/issue.

labels

Sep 16, 2025

@rustbot

r? @ibraheemdev

rustbot has assigned @ibraheemdev.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@414owen 414owen changed the titleImplement ExactSizeIterator for Zip<A, Repeat> Implement ExactSizeIterator for Zip<A, Repeat<B>>

Sep 16, 2025

@yotamofek

Cool idea!

I think they could/should also be TrustedLen, which is even more useful than ESI in cases where you're extending or collecting into, i.e.:

fn extend_trusted(&mut self, iterator: impl iter::TrustedLen<Item = T>) {
let (low, high) = iterator.size_hint();
if let Some(additional) = high {
debug_assert_eq!(
low,
additional,
"TrustedLen iterator's size hint is not exact: {:?}",
(low, high)
);
self.reserve(additional);
unsafe {
let ptr = self.as_mut_ptr();
let mut local_len = SetLenOnDrop::new(&mut self.len);
iterator.for_each(move |element
ptr::write(ptr.add(local_len.current_len()), element);
// Since the loop executes user code which can panic we have to update
// the length every step to correctly drop what we've written.
// NB can't overflow since we would have had to alloc the address space
local_len.increment_len(1);
});
}
} else {
// Per TrustedLen contract a `None` upper bound means that the iterator length
// truly exceeds usize::MAX, which would eventually lead to a capacity overflow anyway.
// Since the other branch already panics eagerly (via `reserve()`) we do the same here.
// This avoids additional codegen for a fallback code path which would eventually
// panic anyway.
panic!("capacity overflow");
}
}

@414owen

Thanks @yotamofek :)

There's already a:

unsafe impl<A, B> TrustedLen for Zip<A, B> where A: TrustedLen, B: TrustedLen, { }

And since Repeat implements TrustedLen itself, I think this case is already covered?

@yotamofek

Ah right, sorry, missed that!

@scottmcm scottmcm added T-libs-api

Relevant to the library API team, which will review and decide on the PR/issue.

needs-fcp

This change is insta-stable, or significant enough to need a team FCP to proceed.

and removed T-libs

Relevant to the library team, which will review and decide on the PR/issue.

labels

Sep 16, 2025

@scottmcm

I think what this really wants it an InfiniteIterator trait for Repeat and RangeFrom and such to implement. Because why Zip<A, Repeat<B>> but not Zip<A, RangeFrom<B>> or Zip<A, RepeatWith<B>> or ...

cuviper

@414owen

@scottmcm I had the same thought, but I figured this would be easier to get through, first.

@VorpalBlade

RepeatWith could also be added to the list @scottmcm mentioned. Such a trait would also scale better outside std in case other iterators outside std can both implement said trait and make use of said trait for their own optimisations.

@414owen

Where: A: ExactSizeIterator, B: Clone,

And the symmetrical instance.

The only other instance for ExactSizeIterator<Zip<_, _>> requires each zipped iterator to be ExactSizeIterator, and since Repeat does not implement ExactSizeIterator, these instances don't overlap with anything.

@414owen

Although for InfiniteIterator my motivation was more for iter.chain(repeat(n)), etc.

The instance for Zip would overlap with the existing ExactSizeIterator trait, and I'm not sure what the best way to tell the compiler they're mutually exclusive traits would be... (edit, looks like I can use negative bounds, nowadays?)

414owen added a commit to 414owen/rust that referenced this pull request

Sep 16, 2025

@414owen

This enables more ExactSizeIterator instances. Previously, in rust-lang#146642, I sought to add specific instances for Zip<Repeat<A>, I> and its symmatrical mirror.

Introducing InfiniteIterator provides much broader support for ExactSizeIterator.

For example,

[1, 2, 3].chain(repeat(1)).take(5)

Will now happily resolve to an instance of ExactSizeIterator.

The downside of this approach is that, to avoid the overlapping instance with Zip, I had to introduce a negative trait bound, which, IIUC, isn't available outside of the compiler.

If anyone knows od a better way to handle the overlapping instances, or a way I can expose something which triggers the negative instance, that would be very helpful.

@ibraheemdev

414owen added a commit to 414owen/rust that referenced this pull request

Sep 17, 2025

@414owen

This enables more ExactSizeIterator instances. Previously, in rust-lang#146642, I sought to add specific instances for Zip<Repeat<A>, I> and its symmatrical mirror.

Introducing InfiniteIterator provides much broader support for ExactSizeIterator.

For example,

[1, 2, 3].chain(repeat(1)).take(5)

Will now happily resolve to an instance of ExactSizeIterator.

The downside of this approach is that, to avoid the overlapping instance with Zip, I had to introduce a negative trait bound, which, IIUC, isn't available outside of the compiler.

If anyone knows of a better way to handle the overlapping instances, or a way I can expose something which triggers the negative instance, that would be very helpful.

There's also a missing symmetrical instance for Chain. Solutions are welcome.

414owen added a commit to 414owen/rust that referenced this pull request

Sep 17, 2025

@414owen

This enables more ExactSizeIterator instances, specifically, those for Take, where the sub-iterator is infinite, and Zip where the one sub-iterator is infinite, and the other is exact sized.

Previously, in rust-lang#146642, I sought to add specific instances for Zip<Repeat<A>, I> and its symmatrical mirror.

Introducing InfiniteIterator provides much broader support for ExactSizeIterator.

For example,

[1, 2, 3].chain(repeat(1)).take(5)

Will now happily resolve to an instance of ExactSizeIterator.

The downside of this approach is that, to avoid the overlapping instance with Zip, I had to introduce a negative trait bound, which, IIUC, isn't available outside of the compiler.

If anyone knows of a better way to handle the overlapping instances, or a way I can expose something which triggers the negative instance, that would be very helpful.

There's also a missing symmetrical instance for Chain. Solutions are welcome.

414owen added a commit to 414owen/rust that referenced this pull request

Sep 17, 2025

@414owen

This enables more ExactSizeIterator instances, specifically, those for Take, where the sub-iterator is infinite, and Zip where the one sub-iterator is infinite, and the other is exact sized.

Previously, in rust-lang#146642, I sought to add specific instances for Zip<Repeat<A>, I> and its symmatrical mirror.

Introducing InfiniteIterator provides much broader support for ExactSizeIterator.

For example,

[1, 2, 3].into_iter().chain(repeat(1)).take(5)

Will now happily resolve to an instance of ExactSizeIterator.

The downside of this approach is that, to avoid the overlapping instance with Zip, I had to introduce a negative trait bound, which, IIUC, isn't available outside of the compiler.

If anyone knows of a better way to handle the overlapping instances, or a way I can expose something which triggers the negative instance, that would be very helpful.

There's also a missing symmetrical instance for Chain. Solutions are welcome.

414owen added a commit to 414owen/rust that referenced this pull request

Sep 17, 2025

@414owen

This enables more ExactSizeIterator instances, specifically, those for Take, where the sub-iterator is infinite, and Zip where the one sub-iterator is infinite, and the other is exact sized.

Previously, in rust-lang#146642, I sought to add specific instances for Zip<Repeat<A>, I> and its symmatrical mirror.

Introducing InfiniteIterator provides much broader support for ExactSizeIterator.

For example,

[1, 2, 3].into_iter().chain(repeat(1)).take(5)

Will now happily resolve to an instance of ExactSizeIterator.

The downside of this approach is that, to avoid the overlapping instance with Zip, I had to introduce a negative trait bound, which, IIUC, isn't available outside of the compiler.

If anyone knows of a better way to handle the overlapping instances, or a way I can expose something which triggers the negative instance, that would be very helpful.

There's also a missing symmetrical instance for Chain. Solutions are welcome.

@414owen