Rust currently has stable support for inclusive-exclusive ranges a .. b
(usually called "ranges") and unstable support for inclusive-inclusive ranges a ... b
(usually called "inclusive ranges"), added by RFC 1192 and tracked at #28237.
The main complaint about inclusive ranges has been that the syntax is confusing (unfortunately it's already used stably in pattern syntax, but we could deprecate that). I want to write an RFC to change the syntax to a p..q b
, where p
and q
are <
or =
to specify exclusive/inclusive (resp.) behavior at that endpoint (for compatibility with the current syntax, p
defaults to =
and q
defaults to <
). This was originally proposed by @glaebhoerl.
Anyway, this thread isn't about syntax -- that was just the motivation. The question is, supposing it's a good idea to fill out the range types so that inclusive and exclusive bounds are supported on both ends, how should this be represented in the libraries? Currently we have
pub struct Range<Idx> {
pub start: Idx,
pub end: Idx,
}
pub struct RangeFrom<Idx> {
pub start: Idx,
}
pub struct RangeTo<Idx> {
pub end: Idx,
}
pub struct RangeFull;
which are stable, and
pub enum RangeInclusive<Idx> {
Empty {
at: Idx,
},
NonEmpty {
start: Idx,
end: Idx,
},
}
pub struct RangeToInclusive<Idx> {
pub end: Idx,
}
which are unstable.
For backwards compatibility, facts like "..
syntax creates a std::ops::Range
" must be preserved:
let r: Range = a .. b;
Below is a list of the ideas I've had so far.
-
The most straightforward approach is to create a combinatorial explosion of structs: one for each combination of left-inclusivity, right-inclusivity, and endedness.
-
Going the other way, we could collapse everything into a single type. Quoting from correspondence with @aturon:
The main reason we had separate structs in the first place was to allow static distinctions about which kind of range you support (rather than, e.g. having to panic on an unsupported style). In hindsight, I think this wasn't worth it. So perhaps another option is to introduce an all-encompassing range type (which, sadly, can't be called
Range
...), and have the new forms generate this type directly, with the existing structs implementingInto<GenericRange>
or whatever. Then most APIs would takeR: Into<GenericRange>
.
- We could introduce a new type,
enum Bound<T> { Inclusive(T), Exclusive(T) }
and useRange<Bound<usize>>
instead ofRange<usize>
, etc. The downsides to this approach are thatRange<Bound<usize>>
is larger thanRange<usize>
due to the discriminants, and that it poses a (possibly solvable) problem with the backwards compatibility requirement mentioned above.
Thoughts?