Skip to content
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

Implement RangeArgument for RangeInclusive and RangeInclusiveTo #32681

Closed
wants to merge 3 commits into from

Conversation

SimonSapin
Copy link
Contributor

This preserves the semantics of RangeArgument::end being exclusive, and converts inclusive ranges to exclusive ones the same way impl<T> ops::Index<ops::RangeInclusive<usize>> for [T] (7eb7c56) does: add one to the end bound and panic on overflow.

This required one change and one limitation:

  • Since the result of that addition is not stored somewhere we can reference, RangeArgument::end (and RangeArgument::start for consistency) was changed to return a copy rather than a reference. This effectively removes some impls where T: !Copy.
  • Since there is no trait with the checked_add method, RangeArgument is implemented for inclusive ranges of each primitive integer types rather than generically.

I think both these points are OK since the only usage of RangeArgument in std is with ranges of usize.

If this change is accepted I’d like to nominate RangeArgument / #30877 for FCP and stabilization. I believe this can happen even if the inclusive range syntax and types are still unstable.

For what it’s worth, I just copied RangeArgument into rust-url to be able to use it there.

CC @alexcrichton & libs team

Indexing with inclusive ranges was added in
7eb7c56
This is because the `end()` method for inclusive ranges (to be added
next) needs to add one, and therefore can not return a reference.
The impls use `checked_add`. Since there is no trait for that method,
the trait is implemented for each primitive integer type rather than
generically.
@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @alexcrichton (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

match *self {
RangeInclusive::Empty { at } => Some(at),
RangeInclusive::NonEmpty { end, .. } => {
Some(end.checked_add(1).expect("inclusive range to maximum usize"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message is wrong, isn't it? When this is expanded for u8, (0...255).end() will panic here. Same for the one above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the best solution without changing the semantics of RangeArgument, but I'm not sure that's the best approach. It feels gross that you can't really use RangeArgument for a range that goes to the rails (like 0...255), which was the main motivation for implementing inclusive ranges. IMO it would be better to change RangeArgument to return an enum like enum Bounded<T> { Inclusive(&T), Exclusive(&T) } (maybe clones instead of references, haven't thought it through) at both ends.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RangeArgument is only used in std with ranges of usize, so this would be 0...4294967295 or 0...18446744073709551615 rather than 0..255. We could change RangeArgument::end (I don’t think changing start is useful) to return an enum as you suggest, but that would only push the panic to each caller (namely Vec::drain, String::drain, and VecDeque::drain) since in-memory collections can not have an item at index usize::MAX (this would make the length >= usize::MAX + 1).

(For what it’s worth, my opinion is that inclusive ranges are not useful for slicing, only as iterators. I’m only making this PR to push for stabilizing RangeArgument.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm well it's easy enough to fix the message to concat!("inclusive range to maximum ", stringify!($Int)) at least.

I see your point but I'm not sure I like privileging usize. And I don't like adding more panics to the standard library. But there's no reason to listen to me! I also understand the desire for stabilization, of course, but it may be premature since I think that range syntax is going to become more flexible (@aturon keeps reminding me to write that RFC...).

@bluss
Copy link
Member

bluss commented Apr 2, 2016

This design makes inclusive ranges extra panicky. with extra branches. We can use a different design to only panic once (out of bounds). I suggested something like that with the IntoCheckedRange design.

Fewer branches and more tailored bounds checking code makes the bounds checks easier to optimize out in more cases.

@alexcrichton
Copy link
Member

This is an interesting change to see to RangeArgument which has otherwise been relatively stable, and this should also be carefully considered as new implementors of that trait will increase the stable surface area of the standard library. Fun things to consider!

cc @rust-lang/libs

@alexcrichton alexcrichton added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Apr 4, 2016
@alexcrichton
Copy link
Member

The libs team discussed this during triage yesterday but we unfortunately didn't reach a firm conclusion. There was some sentiment that if we're going to make the RangeArgument trait more generic we should go "all the way" with perhaps a custom enum type (as I believe has been proposed in previous RFCs), but that's a somewhat large change to these APIs from what it currently is.

@alexcrichton alexcrichton removed the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 4, 2016
@alexcrichton
Copy link
Member

Closing due to inactivity, but feel free to resubmit with comments addressed!

@SimonSapin
Copy link
Contributor Author

It’s not clear how I can address "we unfortunately didn't reach a firm conclusion", but oh well.

For what it’s worth I don’t care much about inclusive ranges for slicing (as opposed to as iterators), I was only hoping to nudge RangeArgument toward stabilization.

@SimonSapin SimonSapin deleted the rangearg branch May 5, 2016 03:01
@oli-obk oli-obk mentioned this pull request Jan 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants