@@ -27,21 +27,17 @@ more dots means more elements.
2727
2828``` rust
2929pub enum RangeInclusive <T > {
30- Empty {
31- at : T ,
32- },
33- NonEmpty {
34- start : T ,
35- end : T ,
36- }
30+ pub start : T ,
31+ pub end : T ,
3732}
3833
3934pub struct RangeToInclusive <T > {
4035 pub end : T ,
4136}
4237```
4338
44- Writing ` a...b ` in an expression desugars to ` std::ops::RangeInclusive::NonEmpty { start: a, end: b } ` . Writing ` ...b ` in an
39+ Writing ` a...b ` in an expression desugars to
40+ ` std::ops::RangeInclusive { start: a, end: b } ` . Writing ` ...b ` in an
4541expression desugars to ` std::ops::RangeToInclusive { end: b } ` .
4642
4743` RangeInclusive ` implements the standard traits (` Clone ` , ` Debug `
@@ -57,6 +53,10 @@ now would be `1. ..` i.e. a floating point number on the left,
5753however, fortunately, it is actually tokenised like ` 1 ... ` , and is
5854hence an error with the current compiler.
5955
56+ This ` struct ` definition is maximally consistent with the existing ` Range ` .
57+ ` a..b ` and ` a...b ` are the same size and have the same fields, just with
58+ the expected difference in semantics.
59+
6060# Drawbacks
6161
6262There's a mismatch between pattern-` ... ` and expression-` ... ` , in that
@@ -66,10 +66,32 @@ semantically.)
6666
6767The ` ... ` vs. ` .. ` distinction is the exact inversion of Ruby's syntax.
6868
69- Having an extra field in a language-level desugaring, catering to one
70- library use-case is a little non-"hygienic". It is especially strange
71- that the field isn't consistent across the different ` ... `
72- desugarings.
69+ Not having a separate marker for ` finished ` or ` empty ` implies a requirement
70+ on ` T ` that it's possible to provide values such that ` b...a ` is an empty
71+ range. But a separate marker is a false invariant: whether a ` finished `
72+ field on the struct or a ` Empty ` variant of an enum, the range ` 10...0 ` still
73+ desugars to a ` RangeInclusive ` with ` finised: false ` or of the ` NonEmpty `
74+ variant. And the fields are public, so even fixing the desugar cannot
75+ guarantee the invariant. As a result, all code using a ` RangeInclusive `
76+ must still check whether a "` NonEmpty ` " or "un` finished ` " is actually finished.
77+ The "can produce an empty range" requirement is not a hardship. It's trivial
78+ for anything that can be stepped forward and backward, as all things which are
79+ iterable in ` std ` are today. But ther are other possibilities as well. The
80+ proof-of-concept implementation for this change is done using the ` replace_one `
81+ and ` replace_zero ` methods of the (existing but unstable) ` Step ` trait, as
82+ ` 1...0 ` is of course an empty range. Something weirder, like walking along a
83+ DAG, could use the fact that ` PartialOrd ` is sufficient, and produce a range
84+ similar in character to ` NaN...NaN ` , which is empty as ` (NaN <= NaN) == false ` .
85+ The exact details of what is required to make a range iterable is outside the
86+ scope of this RFC, and will be decided in the [ ` step_by ` issue] [ step_by ] .
87+
88+ Note that iterable ranges today have a much stronger bound than just
89+ steppability: they require a ` b is-reachable-from a ` predicate (as ` a <= b ` ).
90+ Providing that efficiently for a DAG walk, or even a simpler forward list
91+ walk, is a substantially harder thing to do that providing a pair ` (x, y) `
92+ such that ` !(x <= y) ` .
93+
94+ [ step_by ] : https://github.com/rust-lang/rust/issues/27741
7395
7496# Alternatives
7597
@@ -83,28 +105,25 @@ This RFC proposes single-ended syntax with only an end, `...b`, but not
83105with only a start (` a... ` ) or unconstrained ` ... ` . This balance could be
84106reevaluated for usefulness and conflicts with other proposed syntax.
85107
86- The ` Empty ` variant could be omitted, leaving two options:
87-
88108- ` RangeInclusive ` could be a struct including a ` finished ` field.
109+ This makes it easier for the struct to always be iterable, as the extra
110+ field is set once the ends match. But having the extra field in a
111+ language-level desugaring, catering to one library use-case is a little
112+ non-"hygienic". It is especially strange that the field isn't consistent
113+ across the different ` ... ` desugarings.
114+ - ` RangeInclusive ` could be an enum with ` Empty ` and ` NonEmpty ` variants.
115+ This is cleaner than the ` finished ` field, but makes all uses of the
116+ type substantially more complex. For example, the clamp RFC would
117+ naturally use a ` RangeInclusive ` parameter, but then the
118+ unreliable-` Empty ` vs ` NonEmpty ` distinction provides no value. It does
119+ prevent looking at ` start ` after iteration has completed, but that is
120+ of questionable value when ` Range ` allows it without issue, and disallowing
121+ looking at ` start ` while allowing looking at ` end ` feels inconsistent.
89122- ` a...b ` only implements ` IntoIterator ` , not ` Iterator ` , by
90123 converting to a different type that does have the field. However,
91124 this means that ` a.. .b ` behaves differently to ` a..b ` , so
92125 ` (a...b).map(|x| ...) ` doesn't work (the ` .. ` version of that is
93126 used reasonably often, in the author's experience)
94- - ` a...b ` can implement ` Iterator ` for types that can be stepped
95- backwards: the only case that is problematic things cases like
96- ` x...255u8 ` where the endpoint is the last value in the type's
97- range. A naive implementation that just steps ` x ` and compares
98- against the second value will never terminate: it will yield 254
99- (final state: ` 255...255 ` ), 255 (final state: ` 0...255 ` ), 0 (final
100- state: ` 1...255 ` ). I.e. it will wrap around because it has no way to
101- detect whether 255 has been yielded or not. However, implementations
102- of ` Iterator ` can detect cases like that, and, after yielding ` 255 ` ,
103- backwards-step the second piece of state to ` 255...254 ` .
104-
105- This means that ` a...b ` can only implement ` Iterator ` for types that
106- can be stepped backwards, which isn't always guaranteed, e.g. types
107- might not have a unique predecessor (walking along a DAG).
108127
109128# Unresolved questions
110129
@@ -114,3 +133,5 @@ None so far.
114133
115134* In rust-lang/rfcs #1320 , this RFC was amended to change the ` RangeInclusive `
116135 type from a struct with a ` finished ` field to an enum.
136+ * In rust-lang/rfcs#TODO, this RFC was amended to change the ` RangeInclusive `
137+ type from an enum to a struct with just ` start ` and ` end ` fields.
0 commit comments