@@ -61,19 +61,31 @@ inclusive range is non-empty _iff_ `a <= b`. When the range is iterable,
61
61
a non-empty range will produce at least one item when iterated. Because
62
62
` T::MAX...T::MAX ` is a non-empty range, the iteration needs extra handling
63
63
compared to a half-open ` Range ` . As such, ` .next() ` on an empty range
64
- ` y...y ` will produce the value ` y ` and replace the range with the canonical
65
- empty range for the type. Using methods on the the existing (but unstable)
66
- [ ` Step ` trait] [ step_trait ] , that's ` 1...0 ` for all currently-iterable
67
- value types. Providing such a range is not a burden on the ` T ` type as
64
+ ` y...y ` will produce the value ` y ` and adjust the range such that
65
+ ` !(start <= end) ` . Providing such a range is not a burden on the ` T ` type as
68
66
any such range is acceptable, and only ` PartialOrd ` is required so
69
67
it can be satisfied with an incomparable value ` n ` with ` !(n <= n) ` .
68
+ A caller must not, in general, expect any particular ` start ` or ` end `
69
+ after iterating, and is encouraged to detect empty ranges with
70
+ ` ExactSizeIterator::is_empty ` instead of by observing fields directly.
70
71
71
72
Note that because ranges are not required to be well-formed, they have a
72
73
much stronger bound than just needing successor function: they require a
73
74
` b is-reachable-from a ` predicate (as ` a <= b ` ). Providing that efficiently
74
75
for a DAG walk, or even a simpler forward list walk, is a substantially
75
76
harder thing to do than providing a pair ` (x, y) ` such that ` !(x <= y) ` .
76
77
78
+ Implementation note: For currently-iterable types, the initial implementation
79
+ of this will have the range become ` 1...0 ` after yielding the final value,
80
+ as that can be done using the ` replace_one ` and ` replace_zero ` methods on
81
+ the existing (but unstable) [ ` Step ` trait] [ step_trait ] . It's expected,
82
+ however, that the trait will change to allow more type-appropriate ` impl ` s.
83
+ For example, a ` num::BitInt ` may rather become empty by incrementing ` start ` ,
84
+ as ` Range ` does, since it doesn't to need to worry about overflow. Even for
85
+ primitives, it could be advantageous to choose a different implementation,
86
+ perhaps using ` .overflowing_add(1) ` and swapping on overflow, or ` a...a `
87
+ could become ` (a+1)...a ` where possible and ` a...(a-1) ` otherwise.
88
+
77
89
[ step_trait ] : https://github.com/rust-lang/rust/issues/27741
78
90
79
91
# Drawbacks
@@ -122,11 +134,6 @@ reevaluated for usefulness and conflicts with other proposed syntax.
122
134
this means that ` a.. .b ` behaves differently to ` a..b ` , so
123
135
` (a...b).map(|x| ...) ` doesn't work (the ` .. ` version of that is
124
136
used reasonably often, in the author's experience)
125
- - Different choices for the end of iteration are also possible. While neither
126
- ` start ` nor ` end ` can always have the last value of the iteration while
127
- still producing an empty range (consider what happens with ` MIN...MIN `
128
- and ` MAX...MAX ` ), they could be closer. For example, ` a...a ` could become
129
- ` (a+1)...a ` where possible, and ` a...(a-1) ` otherwise.
130
137
- The name of the ` end ` field could be different, perhaps ` last ` , to reflect
131
138
its different (inclusive) semantics from the ` end ` (exclusive) field on
132
139
the other ranges.
0 commit comments