-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make RangeInclusive just a two-field struct (amend 1192) #1980
Make RangeInclusive just a two-field struct (amend 1192) #1980
Conversation
Rationale: 1. Having the variants make trying to use a RangeInclusive unergonomic. For example, `fn clamp(self, range: RangeInclusive<Self>)` is forced to deal with the `Empty` case. 2. The variants don't actually provide any offsetting safety or additional performance, since everything is pub so it's totally possible for a "`NonEmpty`" range to contain no elements. 3. This makes it more consistent with (half-open) `Range`. 4. The extra flag/variant is not actually needed in order to make it iterable, even using the existing Step trait. And supposing a more cut-down trait, generating `a, b` such that `!(a <= b)` is easier than other fundamental requirements of safe range iterability.
6f20623
to
b9ecb4e
Compare
For the record, here were the arguments in favor of changing it from a struct to an enum: #1192 (comment) |
@durka I agree with #1320 that the enum is superior to the Type history, to save people needing to open all the diffs: pub struct RangeInclusive<T> {
pub start: T,
pub end: T,
pub finished: bool,
} pub enum RangeInclusive<T> {
Empty {
at: T,
},
NonEmpty {
start: T,
end: T,
}
} Proposed here: pub struct RangeInclusive<T> {
pub start: T,
pub end: T,
} |
Wasn't the |
@Stebalien As stated in the amendment, after reaching |
@kennytm I see (although I personally didn't expect an important detail like that to be relegated to the drawbacks section). |
There’s one use case that is not handled by this proposal: looking at which value the iteration has stopped. This is possible with the previous approach and I suppose that’s why the Empty variant had One could try approximating this by having That being said, I have never had any need for |
cc4ef57
to
67cc2cb
Compare
@Stebalien Thanks, that's good feedback. I've moved the behaviour of the patch into the design section (from the double-negative phrasing in drawbacks), as well as updated the alternatives with other possibilities. @nagisa Added that as an explicit drawback. "Good riddance I guess" is pretty much how I felt about it 🙂 |
Ooh, this is great! I don't know why we didn't think of it before. @rfcbot fcp merge |
Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Would it be possible to make the overflow case turn into |
@clarcharr Under the current proposal, all ranges are replaced by The advantage of using I think being able to inspect the exhausted value is a niche use, and is better solved by storing it in an extra variable. let finish_value = range.end.clone();
while let Some(i) = range.next() {
..;
}
// no guarantee what `range` will become here.
// but do whatever you like with `finish_value`. It is much easier than getting a value from Edit. Perhaps the RFC could be relaxed a bit, saying that after calling |
I have no particular attachment to the One thing that is nice about producing I do like the "but it is unspecified what the actual values will be" option, but worry that any later PR that proposed changing the values would be rejected as potentially-breaking, since code could be relying on the values and just compiling all of crates.io wouldn't find such a regression. |
Just to be clear, my proposal for
This still works for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 44: Empty
, no such variant now
🔔 This is now entering its final comment period, as per the review above. 🔔 |
@scottmcm would you mind responding to the example code I gave? It would remove the need for |
@clarcharr Overall, I think what the options here have demonstrated is that they all need some method on Doing it with Maybe the RFC should go with @kennytm's "unspecified empty range" and leave the details to an implementation question or to |
I like leaving it as "unspecified empty," making the only requirement that end > start. Perhaps we could update the RFC to reflect this, and put the 1...0 implementation as just the current plan for implementation, not the spec? |
If we can't come up with a plan here then how is the implementor going to
decide? I don't like just throwing up our hands. If I'm writing some code
that consumes ranges, I need to be able to check whether the range is empty.
Perhaps one way to avoid committing to anything would be to provide a
RangeInclusive::is_empty method, and documenting that it's the only
reliable way to find out.
…On Mon, May 15, 2017 at 3:21 PM, Clar ***@***.***> wrote:
I like leaving it as "unspecified empty," making the only requirement that
end > start. Perhaps we could update the RFC to reflect this, and put the
1...0 implementation as just the current plan for implementation, not the
spec?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1980 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAC3n3rAUmmaKxy1zKyIM72Itat0GGN-ks5r6KXAgaJpZM4NG8Sg>
.
|
@durka RangeInclusive already has an |
@durka I should add that end > start is a pretty non-committal way of describing is_empty while allowing flexibility of implementation |
Updated to make the only post-iteration requirement ( |
The final comment period is now complete. |
Alright the FCP is now complete, and looks like we've had a few tweaks but appears that otherwise nothing major came up. In that case I'm going to merge, thanks again for the RFC @scottmcm! |
Ok I've updated the tracking issue for inclusive ranges to include this RFC as a work item before stabilizing. |
…turon Make RangeInclusive just a two-field struct Not being an enum improves ergonomics and consistency, especially since NonEmpty variant wasn't prevented from being empty. It can still be iterable without an extra "done" bit by making the range have !(start <= end), which is even possible without changing the Step trait. Implements merged rust-lang/rfcs#1980; tracking issue rust-lang#28237. This is definitely a breaking change to anything consuming `RangeInclusive` directly (not as an Iterator) or constructing it without using the sugar. Is there some change that would make sense before this so compilation failures could be compatibly fixed ahead of time? r? @aturon (as FCP proposer on the RFC)
…turon Make RangeInclusive just a two-field struct Not being an enum improves ergonomics and consistency, especially since NonEmpty variant wasn't prevented from being empty. It can still be iterable without an extra "done" bit by making the range have !(start <= end), which is even possible without changing the Step trait. Implements merged rust-lang/rfcs#1980; tracking issue rust-lang#28237. This is definitely a breaking change to anything consuming `RangeInclusive` directly (not as an Iterator) or constructing it without using the sugar. Is there some change that would make sense before this so compilation failures could be compatibly fixed ahead of time? r? @aturon (as FCP proposer on the RFC)
…turon Make RangeInclusive just a two-field struct Not being an enum improves ergonomics and consistency, especially since NonEmpty variant wasn't prevented from being empty. It can still be iterable without an extra "done" bit by making the range have !(start <= end), which is even possible without changing the Step trait. Implements merged rust-lang/rfcs#1980; tracking issue rust-lang#28237. This is definitely a breaking change to anything consuming `RangeInclusive` directly (not as an Iterator) or constructing it without using the sugar. Is there some change that would make sense before this so compilation failures could be compatibly fixed ahead of time? r? @aturon (as FCP proposer on the RFC)
…turon Make RangeInclusive just a two-field struct Not being an enum improves ergonomics and consistency, especially since NonEmpty variant wasn't prevented from being empty. It can still be iterable without an extra "done" bit by making the range have !(start <= end), which is even possible without changing the Step trait. Implements merged rust-lang/rfcs#1980; tracking issue rust-lang#28237. This is definitely a breaking change to anything consuming `RangeInclusive` directly (not as an Iterator) or constructing it without using the sugar. Is there some change that would make sense before this so compilation failures could be compatibly fixed ahead of time? r? @aturon (as FCP proposer on the RFC)
…turon Make RangeInclusive just a two-field struct Not being an enum improves ergonomics and consistency, especially since NonEmpty variant wasn't prevented from being empty. It can still be iterable without an extra "done" bit by making the range have !(start <= end), which is even possible without changing the Step trait. Implements merged rust-lang/rfcs#1980; tracking issue rust-lang#28237. This is definitely a breaking change to anything consuming `RangeInclusive` directly (not as an Iterator) or constructing it without using the sugar. Is there some change that would make sense before this so compilation failures could be compatibly fixed ahead of time? r? @aturon (as FCP proposer on the RFC)
Just a thought... While 1...0 is ok for representing Empty for say,
in principle? And what can an implementation do to change an NonEmpty one into Empty state RangeInclusive for non numeric types? |
…alexcrichton Add Range[Inclusive]::is_empty During rust-lang/rfcs#1980, it was discussed that figuring out whether a range is empty was subtle, and thus there should be a clear and obvious way to do it. It can't just be ExactSizeIterator::is_empty (also unstable) because not all ranges are ExactSize -- such as `Range<i64>` and `RangeInclusive<usize>`. Things to ponder: - Unless this is stabilized first, this makes stabilizing ExactSizeIterator::is_empty more icky, since this hides that. - This is only on `Range` and `RangeInclusive`, as those are the only ones where it's interesting. But one could argue that it should be on more for consistency, or on RangeArgument instead. - The bound on this is PartialOrd, since that works ok (see tests for float examples) and is consistent with `contains`. But ranges like `NAN..=NAN`_are_ kinda weird. - [x] ~~There's not a real issue number on this yet~~
…alexcrichton Add Range[Inclusive]::is_empty During rust-lang/rfcs#1980, it was discussed that figuring out whether a range is empty was subtle, and thus there should be a clear and obvious way to do it. It can't just be ExactSizeIterator::is_empty (also unstable) because not all ranges are ExactSize -- such as `Range<i64>` and `RangeInclusive<usize>`. Things to ponder: - Unless this is stabilized first, this makes stabilizing ExactSizeIterator::is_empty more icky, since this hides that. - This is only on `Range` and `RangeInclusive`, as those are the only ones where it's interesting. But one could argue that it should be on more for consistency, or on RangeArgument instead. - The bound on this is PartialOrd, since that works ok (see tests for float examples) and is consistent with `contains`. But ranges like `NAN..=NAN`_are_ kinda weird. - [x] ~~There's not a real issue number on this yet~~
…alexcrichton Add Range[Inclusive]::is_empty During rust-lang/rfcs#1980, it was discussed that figuring out whether a range is empty was subtle, and thus there should be a clear and obvious way to do it. It can't just be ExactSizeIterator::is_empty (also unstable) because not all ranges are ExactSize -- such as `Range<i64>` and `RangeInclusive<usize>`. Things to ponder: - Unless this is stabilized first, this makes stabilizing ExactSizeIterator::is_empty more icky, since this hides that. - This is only on `Range` and `RangeInclusive`, as those are the only ones where it's interesting. But one could argue that it should be on more for consistency, or on RangeArgument instead. - The bound on this is PartialOrd, since that works ok (see tests for float examples) and is consistent with `contains`. But ranges like `NAN..=NAN`_are_ kinda weird. - [x] ~~There's not a real issue number on this yet~~
Rationale overview:
For example,
fn clamp(self, range: RangeInclusive<Self>)
is forcedto deal with the
Empty
case.performance, since everything is pub so it's totally possible for a
"
NonEmpty
" range to contain no elements.Range
.iterable, even using the existing Step trait. And supposing a more
cut-down trait, generating
a, b
such that!(a <= b)
is easierthan other fundamental requirements of safe range iterability.
Posting this here after discussion on the inclusive ranges tracking issue: rust-lang/rust#28237
A branch with this change implemented: https://github.com/rust-lang/rust/compare/master...scottmcm:rangeinclusive-struct?expand=1
[Edit] Rendered: https://github.com/scottmcm/rfcs/blob/simpler-rangeinclusive/text/1192-inclusive-ranges.md