-
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
ordered ranges 2.0 #1254
ordered ranges 2.0 #1254
Conversation
Would it be possible to remove the |
I'll admit L and R are sort-of artifacts of an earlier design -- the Note that today's Range allows L and R to have different types. In principle this enables searching with different borrows for different end-points. |
Yeah I agree that removing the type parameter does indeed reduce some functionality (e.g. not statically rule out erroneous situations), but making the signatures a little more readable and removing items from the public API are always nice! I also think it's a good point that the left/right bounds could be different types today, but I'm also not 100% convinced that this is necessary. |
Assuming pub struct Unbounded; instead of pub struct Unbounded<'a, Q: ?Sized + 'a>(PhantomData<&'a Q>); |
@apasel422 the associated types for the IntoBound trait don't work out, I think. |
@gankro Yeah, |
I think a type parameters runs afoul of coherence/orphan checking in the IntoIter impl (which is why I didn't use Into). |
Hmm. I'm inclined to agree that we should just store the enums directly rather than use these marker types. We could still parameterize the range over multiple endpoint types though: use std::collections::Bound;
pub struct Range<'a, K: 'a, V: 'a, Min: ?Sized + 'a, Max: ?Sized + 'a> {
map: &'a Map<K, V>,
min: Bound<&'a Min>,
max: Bound<&'a Max>,
} |
Also, what are you planning to name these types if |
Uuugh naming.These can be called RangeBuilder or whatever I guess. |
Will the builder be hardcoded to store Could the builder itself also be parameterized over the collection? Then we could get away with one declaration like pub struct RangeBuilder<'a, C, Min: ?Sized + 'a, Max: ?Sized + 'a = Min> {
collection: C,
min: Bound<&'a Min>,
max: Bound<&'a Max>,
}
impl<'a, 'b, K, V, Min: ?Sized, Max: ?Sized> IntoIterator for RangeBuilder<'a, &'b BTreeMap<K, V>, Min, Max> where
K: Borrow<Min> + Borrow<Max> + Ord,
Min: Ord,
Max: Ord,
{
type IntoIter = btree_map::Range<'b, K, V>;
...
}
impl<'a, 'b, K, V, Min: ?Sized, Max: ?Sized> IntoIterator for RangeBuilder<'a, &'b mut BTreeMap<K, V>, Min, Max> where
K: Borrow<Min> + Borrow<Max> + Ord,
Min: Ord,
Max: Ord,
{
type IntoIter = btree_map::RangeMut<'b, K, V>;
...
} This would also enable more code reuse for external crates that want to provide the ordered query API. |
Let's question something we never do: Should the range really be double ended? I'm reasonably sure it has an irremovable overhead to support, and rust often claims to go for the least costly abstractions. Double ended iterators are nice where they are cheap, but here it has a cost (or is this wrong?). On the whole I think the range builder thing is fine, it's something users will recognize, even if it's not from other collections. |
@bluss That's another benefit of making range a builder: we can do that as different finalizers if it proves useful. By default I'd like to assume they'll be DoubleEnded, and investigate during/after implementation of the functionality that most would expect. |
@apasel422 What do you suggest that Drain's RangeBuilder contains? A newtype |
@gankro Missed this earlier. I'm not sure I totally understand your question. |
@gankro That sounds great |
@apasel422 If there is only one DrainBuilder, and |
🔔 This RFC is now entering its week-long final comment period 🔔 |
My personal preference would be to remove the two |
@alexcrichton what do you expect the behaviour of |
I'd want the upper bound of |
@alexcrichton I would expect the "other" sensible behavior. Listing a bunch of conditions looks very much like a conjunction to me, so |
I'm late to the party here, but I have some concerns about this RFC. First, I want to say that in isolation, I think a builder design like this is a pretty slick way of solving the problem. Kudos! However, I'm pretty concerned about the global design ramifications here. In particular, we've gradually been growing our range notation -- which is meant to be a generic way to express ranges, usable for a variety of purposes. We recently merged an RFC to add inclusive ranges. And we use this syntax heavily for indexed collections. It seems a real shame to have a completely different way of expressing ranges in the two cases, just because the syntax we happened to settle on doesn't support exclusivity on the left. I feel like this is a sign that we dropped the ball on the design of the range syntax, not accounting for the full, ahem, range of uses. In particular, if we had a design that allowed you to choose between exclusivity/inclusivity on both the left and the right -- basically, the equivalent to notation like Put differently, I fear it's going to be extremely annoying to get used to doing things like Given that inclusive ranges have not yet landed, I wonder whether we should take a step back and reconsider an overall range notation design that can express the full suite of ranges. |
Strawman proposal: the base syntax for ranges is
The modifier can be omitted on either or both sides, on the left it defaults to I actually like the currently accepted (This syntax seems so logical that I'm almost sure somebody has proposed it before: if so, my apologies!) |
@glaebhoerl Something along those lines seems pretty reasonable to me. I think in the previous round (Not sure whether we want to re-start the whole bikeshed within this RFC. But I'm going to raise my cross-cutting concerns here with the core team, since it feels like the incremental design work on the syntax side has not fully taken library concerns into account.) |
@aturon I feel somewhat similar. While reading the RFC, I thought "it's really unfortunate we can't reuse range notation for this." I will say though, that I do actually really like the API proposed. The bummer, as you say, is having two different ways to express ranges. Spending some extra time to see if there exists one way to express ranges might be worth it. |
The libs team discussed this RFC today and the decision was to move it out of FCP for now, but perhaps leave it open for a little more discussion. In light of @aturon's recent comment there's been some more thinking about this, and one idea brought up by @wycats was to perhaps have method syntax plus a builder to "emulate" what sugar one day might buy us. For example: fn range<T>() -> RangeBuilder<T> { /* ... */ }
impl<T> RangeBuilder<T> {
fn inclusive_left(&mut self, t: T) -> &mut Self { /* ... */ }
fn exclusive_left(&mut self, t: T) -> &mut Self { /* ... */ }
fn inclusive_right(&mut self, t: T) -> &mut Self { /* ... */ }
fn exclusive_right(&mut self, t: T) -> &mut Self { /* ... */ }
}
impl<T> RangeTrait<T> for RangeBuilder<T> {
/* ... */
} With this we'd have something like: impl<K, V> BTreeMap<K, V> {
fn range<T: RangeTrait<U>, U>(&self, t: T) -> Range<U> where K: Borrow<U> {
/* ... */
}
} The current range syntax we have (e.g. The general idea would be to introduce a more verbose library-based implementation of "all kinds of ranges" which can be supplanted with special sugar syntax later on if it becomes available. All along the way the Thoughts @gankro? |
Seem basically fine (the methods should be I'm a bit ambivalent about the temporary assymetry here. |
But wouldn't that mean we would end up, one day, with two kinds of syntax (not counting library methods here) to say whether the right-hand end of a range is inclusive? We'd have, e.g., |
@glaebhoerl I think that's a rather refreshing proposal, to not introduce inclusive range syntax. We could consider unshackling |
|
@gankro yeah the details of the actual API itself will likely be tweaked a bit, and I'd be fine with consumption so long as it turns out nicely. @RalfJung yeah I agree that it would lead us to a situation of "two ways to do the same thing", but I don't really see it as all that bad as it would in theory be a very well known and mechanical conversion. Additionally we'd perhaps deprecate the "builder API" if sugar was available instead. @bluss that's not a bad idea as well! I think the major question there is in terms of the ergonomics and what the downstream traits would look like for consuming a number of ranges, but otherwise it may obviate the need for |
Personally, I think any decent API for this problem shouldn't require importing anything. |
Are there any note worthy implementations of this feature in other languages? |
This RFC is dead, pending the lang team investigating more complete range syntax. |
This PR implements [RFC 1192](https://github.com/rust-lang/rfcs/blob/master/text/1192-inclusive-ranges.md), which is triple-dot syntax for inclusive range expressions. The new stuff is behind two feature gates (one for the syntax and one for the std::ops types). This replaces the deprecated functionality in std::iter. Along the way I simplified the desugaring for all ranges. This is my first contribution to rust which changes more than one character outside of a test or comment, so please review carefully! Some of the individual commit messages have more of my notes. Also thanks for putting up with my dumb questions in #rust-internals. - For implementing `std::ops::RangeInclusive`, I took @Stebalien's suggestion from rust-lang/rfcs#1192 (comment). It seemed to me to make the implementation easier and increase type safety. If that stands, the RFC should be amended to avoid confusion. - I also kind of like @glaebhoerl's [idea](rust-lang/rfcs#1254 (comment)), which is unified inclusive/exclusive range syntax something like `x>..=y`. We can experiment with this while everything is behind a feature gate. - There are a couple of FIXMEs left (see the last commit). I didn't know what to do about `RangeArgument` and I haven't added `Index` impls yet. Those should be discussed/finished before merging. cc @gankro since you [complained](https://www.reddit.com/r/rust/comments/3xkfro/what_happened_to_inclusive_ranges/cy5j0yq) cc #27777 #30877 #1192 rust-lang/rfcs#1254 relevant to #28237 (tracking issue)
@gankro Has this been resolved? |
Replace
range(Bound::Excluded(&min), Bound::Included(&max))
withrange().ge(&min).lt(&max)
.Add BTreeMap::drain as appropriate.
Flesh out the general "ordered query" story.
Rendered