Add Step::forward/backward_overflowing to enable RangeInclusive loop optimizations#155114
Add Step::forward/backward_overflowing to enable RangeInclusive loop optimizations#155114pitaj wants to merge 3 commits into
Step::forward/backward_overflowing to enable RangeInclusive loop optimizations#155114Conversation
This comment has been minimized.
This comment has been minimized.
f26532b to
d0c1f8d
Compare
This comment has been minimized.
This comment has been minimized.
d0c1f8d to
39e74a3
Compare
|
We discussed this in the @rust-lang/libs-api meeting. The If the |
|
Right, I forgot this implements Eq even though I implemented it manually 🤦♂️. @Amanieu I'm not sure it gains much over just going back to the derive, would that be acceptable? |
|
The derive is also acceptable. We explicitly document that |
It implements DoubleEndedIterator but regardless yeah I'll change it back to the derive |
|
@rustbot ready |
|
I found a bug with this PR. fn main() {
let mut range = 255_u8..=255_u8;
let _ = range.next();
println!("{range:?}");
println!("{}", range.contains(&100_u8));
}Output of above code on nightly: Output of above code with this PR: (The desired behavior of the |
|
And here's some strange behavior that's possibly a bug: fn main() {
let mut range = 0_usize..=0_usize;
let _ = range.next_back();
println!("{range:?}");
let data = [0; 1000];
println!("{:?}", &data[range]);
}Output of above code on nightly: Output of above code with this PR: |
Good find on
This is expected behavior from the new overflowing behavior. Given that the values of
I think the I also changed Nominating for libs-api approval of those changes |
|
This was discussed the @rust-lang/libs-api meeting. We're happy with the @bors try |
|
@bors r- https://triage.rust-lang.org/gha-logs/rust-lang/rust/83976413749#L2026-06-29T04:53:02.5947801Z |
|
This pull request was unapproved. This PR was contained in a rollup (#158538), which was unapproved. |
|
The failed test was added in #155421 |
|
I believe that guaranteeing (as stated in #155421) that the This is because, with this PR, checking whether a Nominating to decide how to resolve this dilemma, possibly reverting #155421. |
|
This PR changed when RangeInclusive becomes exhausted (now happens only when the bound overflows). let mut exhausted: legacy::RangeInclusive<u8> = 255..=255;
exhausted.next();So I don't think this needs to be looked at by libs-api again, there already okayed the exhausted change. Your concern seems to be confusing the exhausted state with being empty. |
|
@pitaj I am interpreting "exhaust" to mean the operation of "calling .next() until the iterator has produced all elements it could ever produce". I think of it as a publicly visible property, not a private implementation detail stored in the field named At the very least, I think we should let libs-api decide what "exhaust" in the documentation should mean. |
|
We discussed this in the @rust-lang/libs-api meeting. We think that the best solution here is to relax the guarantees around panicking. Instead of guaranteeing a panic if the range is exhausted, we would instead only guarantee that the result is either a panic or an empty range. |
|
Changing this to panic only sometimes feels like a serious footgun. Perhaps it could be changed to always return an empty range instead? |
|
I don't think always returning an empty range is possible in a generic context. I'm not too concerned. Realistically, nobody is going to ever call |
True.
You can construct such a test with |
but RangeInclusive loops do not
and optimize the RangeInclusive iterator implementation with them This changes the `exhausted` field to represent an overflow flag for the bounds, essentially acting as an extra bit for `Idx`. This was found to enable optimizations previously only applicable to the exclusive-ended Range type. - change end_bound to return Excluded(start) when exhausted - add contains to tests - make into_bounds panic when exhausted matches From<legacy::RangeInclusive<T>> for RangeInclusive<T> - add tests for new Step impls
so it is compatible with the new exhaustion behavior. And fix the examples to exercise that behavior.
c83fca5 to
dda08ec
Compare
|
This PR was rebased onto a different main 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. |
|
@rustbot ready |
I added these - turns out it was a very good idea! Found a few copypaste bugs that slipped past us, which have now been fixed. |
|
I made a PR #158770, which only touches the |
|
Did you happen to test whether something along the lines of @matthieu-m's proposal at rust-lang/rfcs#3550 (comment) happens to get the desired optimization? Sorry if this has been discussed already, I haven't been keeping up with this thread but noticed #45222 didn't seem to be linked anywhere. |
|
That optimization would only be applicable to the new RangeInclusive iterator, whereas this one applies to both legacy and new. |
As per rust-lang#155114 (comment), this `From` impl no longer guarantees panicking for exhausted iterators. Instead, it only guarantees that the conversion will either panic or produce an empty range. This is done so that we can optimize the implementation of `legacy::RangeInclusive` in a way such that we cannot check if it has been exhausted in a generic context without a `Step` and/or `PartialOrd` bound.
…=JohnTitor Weaken guarantee for `From<legacy::RangeInclusive> for RangeInclusive` As per rust-lang#155114 (comment), this `From` impl no longer guarantees panicking for exhausted iterators. Instead, it only guarantees that the conversion will either panic or produce an empty range. This is done so that we can optimize the implementation of `legacy::RangeInclusive` in a way such that we cannot check if it has been exhausted in a generic context without a `Step` and/or `PartialOrd` bound. If this PR and/or rust-lang#155114 merges after the 1.98.0 beta branch promotion from the main branch, then this will need a beta backport, since this PR changes the stable guarantee previously made in rust-lang#155421. This PR conflicts with and/or blocks rust-lang#155114.
View all comments
ACP: rust-lang/libs-team#767
This adds new required methods to the Step trait:
It was found that using these to implement RangeInclusive's Iterator impl enabled optimizations previously only applicable to the exclusive-ended
Range.This required changing how "exhaustion" works for
RangeInclusive. I've nominated this for libs-api discussion because of one insta-stable change:The new implementations now only set
exhaustedwhen overflow occurs, andstartis now advanced pastendotherwise. I doubt anyone depends on the prior behavior, but it's probably worth a crater run.The exhaustion changes also affect
Debugbut my understanding is that debug formatting is never guaranteed stable.I have now changed the
nthimpls to use the new functions as well.r? libs