Skip to content

Commit d6ea75b

Browse files
committed
Unspecify the post-iteration value of RangeInclusive
1 parent 0fdca13 commit d6ea75b

File tree

1 file changed

+16
-9
lines changed

1 file changed

+16
-9
lines changed

text/1192-inclusive-ranges.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,31 @@ inclusive range is non-empty _iff_ `a <= b`. When the range is iterable,
6161
a non-empty range will produce at least one item when iterated. Because
6262
`T::MAX...T::MAX` is a non-empty range, the iteration needs extra handling
6363
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
6866
any such range is acceptable, and only `PartialOrd` is required so
6967
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.
7071

7172
Note that because ranges are not required to be well-formed, they have a
7273
much stronger bound than just needing successor function: they require a
7374
`b is-reachable-from a` predicate (as `a <= b`). Providing that efficiently
7475
for a DAG walk, or even a simpler forward list walk, is a substantially
7576
harder thing to do than providing a pair `(x, y)` such that `!(x <= y)`.
7677

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+
7789
[step_trait]: https://github.com/rust-lang/rust/issues/27741
7890

7991
# Drawbacks
@@ -122,11 +134,6 @@ reevaluated for usefulness and conflicts with other proposed syntax.
122134
this means that `a.. .b` behaves differently to `a..b`, so
123135
`(a...b).map(|x| ...)` doesn't work (the `..` version of that is
124136
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.
130137
- The name of the `end` field could be different, perhaps `last`, to reflect
131138
its different (inclusive) semantics from the `end` (exclusive) field on
132139
the other ranges.

0 commit comments

Comments
 (0)