Skip to content

Conversation

hkBst
Copy link
Member

@hkBst hkBst commented Sep 10, 2025

This removes two cases of infinite looping from Repeat:

  • last: By viewing the iterator as returning None after omega calls to next, this method can simply return the repeated element.

  • count: From its docs: """The method does no guarding against overflows, so counting elements of an iterator with more than usize::MAX elements either produces the wrong result or panics. If overflow checks are enabled, a panic is guaranteed.""", so a panic'ing impl is allowed by the docs, and is more honest than an infinite loop.

@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 10, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 10, 2025

r? @jhpratt

rustbot has assigned @jhpratt.
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

@workingjubilee
Copy link
Member

@hkBst I think the count change is obviously correct (since ω is larger than every natural number, usize::MAX included) but the last change is debatable. Maybe they should be split? idk, up to you.

@hkBst
Copy link
Member Author

hkBst commented Sep 11, 2025

@workingjubilee I think they are both debatable. Happy to split if it should become clear that that would be beneficial!

@jhpratt
Copy link
Member

jhpratt commented Sep 13, 2025

imo there is no last element given that the iterator is infinite, even if all elements are identical. Is there any precedent for saying otherwise? The docs for len seem to indicate that it should loop forever, as there's no note about overflowing usize::MAX like there is for count.

@hkBst
Copy link
Member Author

hkBst commented Sep 13, 2025

@jhpratt that is certainly a valid viewpoint, but I would argue that in that case a panic is more appropriate than a foreseeable infinite loop.

From last's docs:

Consumes the iterator, returning the last element.

There is currently no allowance for panicking or looping, so imagine that you exhaust the infinite iterator (supertask), is there a possible element that could be returned? Since all elements are the same, there is only one choice. The other option is None, but that would seem to be reserved for empty sequences...

This method will evaluate the iterator until it returns None. While doing so, it keeps track of the current element. After None is returned, last() will then return the last element it saw.

The previous analysis seems consistent with this elaboration, although it does not readily transfer to other infinite iterators. Those should probably panic instead.

@hanna-kruppe
Copy link
Contributor

Aside from the question of what’s “correct” per API contract, the more important question to me is what’s helpful.

For count, a panic with a descriptive message is helpful (especially with the addition of #[track_caller]) because it makes tracking down a bug a bit easier.

For last, returning something instead of looping forever doesn’t seem helpful. If you know you have a Repeat on your hands, you can just call next() instead of last(). If you don’t know that, you probably have a bug because code that expects a finite iterator got its hands in an infinite iterator. Returning something from Repeat::last() can mask that bug until you change the iterator chain, so it seems unhelpful. (However, a panic would be helpful if it can otherwise be justified.)

@jhpratt
Copy link
Member

jhpratt commented Sep 13, 2025

Personally I agree, but it's not my prerogative to add the possibility of new panics or different behavior to documentation.

@hkBst
Copy link
Member Author

hkBst commented Sep 13, 2025

I would be happy with a panic for last if that is preferred.

@jhpratt
Copy link
Member

jhpratt commented Sep 15, 2025

@rust-lang/libs-api, what are your thoughts on this? As currently written, I don't see room for anything other than an infinite loop for last, so this would technically be a change.

@theemathas
Copy link
Contributor

Given that Repeat already implements DoubleEndedIterator, I think that the ship has sailed. last() should not infinite loop, in order to be consistent with next_back().

@Amanieu
Copy link
Member

Amanieu commented Sep 15, 2025

Given that Repeat already implements DoubleEndedIterator, I think that the ship has sailed. last() should not infinite loop, in order to be consistent with next_back().

I agree, the DoubleEndedIterator essentially means that Repeat is conceptually a double-ended iterator with an infinite number of elements in the middle, but a well-defined first and last element.

@jhpratt
Copy link
Member

jhpratt commented Sep 16, 2025

I agree, the DoubleEndedIterator essentially means that Repeat is conceptually a double-ended iterator with an infinite number of elements in the middle, but a well-defined first and last element.

That's an interesting way of thinking of it. Given this and the 👍 from the8472, I'm taking that as approval from two team members to move forward with that.

@hkBst Can you add #[track_caller] to count method? This will more accurately show where the issue is in a program when someone inevitably calls it. r=me with that.

@rustbot
Copy link
Collaborator

rustbot commented Sep 16, 2025

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@hkBst
Copy link
Member Author

hkBst commented Sep 16, 2025

@jhpratt added track_caller to count, and fixed a small typo.

@jhpratt
Copy link
Member

jhpratt commented Sep 16, 2025

Perfect, thanks!

@bors r+ rollup

@rustbot label +relnotes

@bors
Copy link
Collaborator

bors commented Sep 16, 2025

📌 Commit c89b6a9 has been approved by jhpratt

It is now in the queue for this repository.

@rustbot rustbot added the relnotes Marks issues that should be documented in the release notes of the next release. label Sep 16, 2025
@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 16, 2025
jhpratt added a commit to jhpratt/rust that referenced this pull request Sep 17, 2025
Iterator repeat: no infinite loop for `last` and `count`

This removes two cases of infinite looping from [`Repeat`](https://doc.rust-lang.org/nightly/std/iter/struct.Repeat.html):
- [`last`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.last): By viewing the iterator as returning None after [omega](https://en.wikipedia.org/wiki/Ordinal_number) calls to `next`, this method can simply return the repeated element.

- [`count`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.count): From its docs: """The method does no guarding against overflows, so counting elements of an iterator with more than [usize::MAX](https://doc.rust-lang.org/nightly/std/primitive.usize.html#associatedconstant.MAX) elements either produces the wrong result or panics. If overflow checks are enabled, a panic is guaranteed.""", so a panic'ing impl is allowed by the docs, and is more honest than an infinite loop.
bors added a commit that referenced this pull request Sep 17, 2025
Rollup of 9 pull requests

Successful merges:

 - #144871 (Stabilize `btree_entry_insert` feature)
 - #145181 (remove FIXME block from `has_significant_drop`, it never encounters inference variables)
 - #145838 (don't apply temporary lifetime extension rules to non-extended `super let`)
 - #146259 (Suggest removing Box::new instead of unboxing it)
 - #146410 (Iterator repeat: no infinite loop for `last` and `count`)
 - #146460 (Add tidy readme)
 - #146581 (Detect attempt to use var-args in closure)
 - #146588 (tests/run-make: Update list of statically linked musl targets)
 - #146647 (Move `#[rustc_coherence_is_core]` to the `crate_level` file)

r? `@ghost`
`@rustbot` modify labels: rollup
@Jules-Bertholet
Copy link
Contributor

Jules-Bertholet commented Sep 17, 2025

The new behaviors of these two methods should be documented with a doc-comment, at the very least; they contradict the docs on the Iterator trait.

@Jules-Bertholet
Copy link
Contributor

I also think it’s quite strange and unexpected that with this PR, Repeat::last() and Repeat::count() will behave very differently from <&mut Repeat>::last()/<&mut Repeat>::count().

Zalathar added a commit to Zalathar/rust that referenced this pull request Sep 17, 2025
Iterator repeat: no infinite loop for `last` and `count`

This removes two cases of infinite looping from [`Repeat`](https://doc.rust-lang.org/nightly/std/iter/struct.Repeat.html):
- [`last`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.last): By viewing the iterator as returning None after [omega](https://en.wikipedia.org/wiki/Ordinal_number) calls to `next`, this method can simply return the repeated element.

- [`count`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.count): From its docs: """The method does no guarding against overflows, so counting elements of an iterator with more than [usize::MAX](https://doc.rust-lang.org/nightly/std/primitive.usize.html#associatedconstant.MAX) elements either produces the wrong result or panics. If overflow checks are enabled, a panic is guaranteed.""", so a panic'ing impl is allowed by the docs, and is more honest than an infinite loop.
@hkBst
Copy link
Member Author

hkBst commented Sep 17, 2025

@Jules-Bertholet I suppose you're correct, since &mut Iterator uses this impl which does not override count and last... I'll see about adding those and some tests.

bors added a commit that referenced this pull request Sep 17, 2025
Rollup of 14 pull requests

Successful merges:

 - #142807 (libtest: expose --fail-fast as an unstable command-line option)
 - #144871 (Stabilize `btree_entry_insert` feature)
 - #145071 (Update the minimum external LLVM to 20)
 - #145181 (remove FIXME block from `has_significant_drop`, it never encounters inference variables)
 - #145660 (initial implementation of the darwin_objc unstable feature)
 - #145838 (don't apply temporary lifetime extension rules to non-extended `super let`)
 - #146259 (Suggest removing Box::new instead of unboxing it)
 - #146410 (Iterator repeat: no infinite loop for `last` and `count`)
 - #146460 (Add tidy readme)
 - #146552 (StateTransform: Do not renumber resume local.)
 - #146564 (Remove Rvalue::Len again.)
 - #146581 (Detect attempt to use var-args in closure)
 - #146588 (tests/run-make: Update list of statically linked musl targets)
 - #146631 (cg_llvm: Replace some DIBuilder wrappers with LLVM-C API bindings (part 3))

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit cefd932 into rust-lang:master Sep 17, 2025
10 checks passed
@rustbot rustbot added this to the 1.92.0 milestone Sep 17, 2025
rust-timer added a commit that referenced this pull request Sep 17, 2025
Rollup merge of #146410 - hkBst:repeat-1, r=jhpratt

Iterator repeat: no infinite loop for `last` and `count`

This removes two cases of infinite looping from [`Repeat`](https://doc.rust-lang.org/nightly/std/iter/struct.Repeat.html):
- [`last`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.last): By viewing the iterator as returning None after [omega](https://en.wikipedia.org/wiki/Ordinal_number) calls to `next`, this method can simply return the repeated element.

- [`count`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.count): From its docs: """The method does no guarding against overflows, so counting elements of an iterator with more than [usize::MAX](https://doc.rust-lang.org/nightly/std/primitive.usize.html#associatedconstant.MAX) elements either produces the wrong result or panics. If overflow checks are enabled, a panic is guaranteed.""", so a panic'ing impl is allowed by the docs, and is more honest than an infinite loop.
@hkBst
Copy link
Member Author

hkBst commented Sep 17, 2025

@Jules-Bertholet Actually it does not seem possible to delegate any methods that take self from a by_ref iterator to the real iterator, as it ends up trying to move something behind a ref. The current impl cheats by reimplementing fold (which moves) in terms of try_fold (which mutates). For Repeat, fold should panic, but try_fold cannot, as it could short-circuit depending on its arguments, so there does not seem to be a good way to fix this. Perhaps by-ref iterators should be deprecated as the hack that they are? Or perhaps only work for finite iterators?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants