Skip to content

Commit f166bd9

Browse files
committed
Make RangeInclusive just a two-field struct
Not being an enum improves ergonomics, especially since NonEmpty could be Empty. It can still be iterable without an extra "done" bit by making the range have !(start <= end), which is even possible without changing the Step trait. Implements RFC 1980
1 parent 0bd9e1f commit f166bd9

File tree

8 files changed

+134
-249
lines changed

8 files changed

+134
-249
lines changed

src/libcollections/range.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,10 @@ impl<T> RangeArgument<T> for Range<T> {
106106
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
107107
impl<T> RangeArgument<T> for RangeInclusive<T> {
108108
fn start(&self) -> Bound<&T> {
109-
match *self {
110-
RangeInclusive::Empty{ ref at } => Included(at),
111-
RangeInclusive::NonEmpty { ref start, .. } => Included(start),
112-
}
109+
Included(&self.start)
113110
}
114111
fn end(&self) -> Bound<&T> {
115-
match *self {
116-
RangeInclusive::Empty{ ref at } => Excluded(at),
117-
RangeInclusive::NonEmpty { ref end, .. } => Included(end),
118-
}
112+
Included(&self.end)
119113
}
120114
}
121115

src/libcore/iter/range.rs

+52-120
Original file line numberDiff line numberDiff line change
@@ -403,61 +403,35 @@ impl<A: Step + Clone> Iterator for StepBy<A, ops::RangeInclusive<A>> {
403403

404404
#[inline]
405405
fn next(&mut self) -> Option<A> {
406-
use ops::RangeInclusive::*;
407-
408-
// this function has a sort of odd structure due to borrowck issues
409-
// we may need to replace self.range, so borrows of start and end need to end early
410-
411-
let (finishing, n) = match self.range {
412-
Empty { .. } => return None, // empty iterators yield no values
413-
414-
NonEmpty { ref mut start, ref mut end } => {
415-
let rev = self.step_by.is_negative();
416-
417-
// march start towards (maybe past!) end and yield the old value
418-
if (rev && start >= end) ||
419-
(!rev && start <= end)
420-
{
421-
match start.step(&self.step_by) {
422-
Some(mut n) => {
423-
mem::swap(start, &mut n);
424-
(None, Some(n)) // yield old value, remain non-empty
425-
},
426-
None => {
427-
let mut n = end.clone();
428-
mem::swap(start, &mut n);
429-
(None, Some(n)) // yield old value, remain non-empty
430-
}
431-
}
432-
} else {
433-
// found range in inconsistent state (start at or past end), so become empty
434-
(Some(end.replace_zero()), None)
435-
}
436-
}
437-
};
406+
let rev = self.step_by.is_negative();
438407

439-
// turn into an empty iterator if we've reached the end
440-
if let Some(end) = finishing {
441-
self.range = Empty { at: end };
408+
if (rev && self.range.start >= self.range.end) ||
409+
(!rev && self.range.start <= self.range.end)
410+
{
411+
match self.range.start.step(&self.step_by) {
412+
Some(n) => {
413+
Some(mem::replace(&mut self.range.start, n))
414+
},
415+
None => {
416+
let last = self.range.start.replace_one();
417+
self.range.end.replace_zero();
418+
self.step_by.replace_one();
419+
Some(last)
420+
},
421+
}
422+
}
423+
else {
424+
None
442425
}
443-
444-
n
445426
}
446427

447428
#[inline]
448429
fn size_hint(&self) -> (usize, Option<usize>) {
449-
use ops::RangeInclusive::*;
450-
451-
match self.range {
452-
Empty { .. } => (0, Some(0)),
453-
454-
NonEmpty { ref start, ref end } =>
455-
match Step::steps_between(start,
456-
end,
457-
&self.step_by) {
458-
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
459-
None => (0, None)
460-
}
430+
match Step::steps_between(&self.range.start,
431+
&self.range.end,
432+
&self.step_by) {
433+
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
434+
None => (0, None)
461435
}
462436
}
463437
}
@@ -583,56 +557,27 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> where
583557

584558
#[inline]
585559
fn next(&mut self) -> Option<A> {
586-
use ops::RangeInclusive::*;
587-
588-
// this function has a sort of odd structure due to borrowck issues
589-
// we may need to replace self, so borrows of self.start and self.end need to end early
590-
591-
let (finishing, n) = match *self {
592-
Empty { .. } => (None, None), // empty iterators yield no values
593-
594-
NonEmpty { ref mut start, ref mut end } => {
595-
if start == end {
596-
(Some(end.replace_one()), Some(start.replace_one()))
597-
} else if start < end {
598-
let mut n = start.add_one();
599-
mem::swap(&mut n, start);
600-
601-
// if the iterator is done iterating, it will change from
602-
// NonEmpty to Empty to avoid unnecessary drops or clones,
603-
// we'll reuse either start or end (they are equal now, so
604-
// it doesn't matter which) to pull out end, we need to swap
605-
// something back in
606-
607-
(if n == *end { Some(end.replace_one()) } else { None },
608-
// ^ are we done yet?
609-
Some(n)) // < the value to output
610-
} else {
611-
(Some(start.replace_one()), None)
612-
}
613-
}
614-
};
615-
616-
// turn into an empty iterator if this is the last value
617-
if let Some(end) = finishing {
618-
*self = Empty { at: end };
560+
use cmp::Ordering::*;
561+
562+
match self.start.partial_cmp(&self.end) {
563+
Some(Less) => {
564+
let n = self.start.add_one();
565+
Some(mem::replace(&mut self.start, n))
566+
},
567+
Some(Equal) => {
568+
let last = self.start.replace_one();
569+
self.end.replace_zero();
570+
Some(last)
571+
},
572+
_ => None,
619573
}
620-
621-
n
622574
}
623575

624576
#[inline]
625577
fn size_hint(&self) -> (usize, Option<usize>) {
626-
use ops::RangeInclusive::*;
627-
628-
match *self {
629-
Empty { .. } => (0, Some(0)),
630-
631-
NonEmpty { ref start, ref end } =>
632-
match Step::steps_between_by_one(start, end) {
633-
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
634-
None => (0, None),
635-
}
578+
match Step::steps_between_by_one(&self.start, &self.end) {
579+
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
580+
None => (0, None),
636581
}
637582
}
638583
}
@@ -644,33 +589,20 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> where
644589
{
645590
#[inline]
646591
fn next_back(&mut self) -> Option<A> {
647-
use ops::RangeInclusive::*;
648-
649-
// see Iterator::next for comments
650-
651-
let (finishing, n) = match *self {
652-
Empty { .. } => return None,
653-
654-
NonEmpty { ref mut start, ref mut end } => {
655-
if start == end {
656-
(Some(start.replace_one()), Some(end.replace_one()))
657-
} else if start < end {
658-
let mut n = end.sub_one();
659-
mem::swap(&mut n, end);
660-
661-
(if n == *start { Some(start.replace_one()) } else { None },
662-
Some(n))
663-
} else {
664-
(Some(end.replace_one()), None)
665-
}
666-
}
667-
};
668-
669-
if let Some(start) = finishing {
670-
*self = Empty { at: start };
592+
use cmp::Ordering::*;
593+
594+
match self.start.partial_cmp(&self.end) {
595+
Some(Less) => {
596+
let n = self.end.sub_one();
597+
Some(mem::replace(&mut self.end, n))
598+
},
599+
Some(Equal) => {
600+
let last = self.end.replace_zero();
601+
self.start.replace_one();
602+
Some(last)
603+
},
604+
_ => None,
671605
}
672-
673-
n
674606
}
675607
}
676608

src/libcore/ops.rs

+8-32
Original file line numberDiff line numberDiff line change
@@ -2271,7 +2271,7 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
22712271
/// ```
22722272
/// #![feature(inclusive_range,inclusive_range_syntax)]
22732273
/// fn main() {
2274-
/// assert_eq!((3...5), std::ops::RangeInclusive::NonEmpty{ start: 3, end: 5 });
2274+
/// assert_eq!((3...5), std::ops::RangeInclusive{ start: 3, end: 5 });
22752275
/// assert_eq!(3+4+5, (3...5).sum());
22762276
///
22772277
/// let arr = [0, 1, 2, 3];
@@ -2281,45 +2281,23 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
22812281
/// ```
22822282
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
22832283
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
2284-
pub enum RangeInclusive<Idx> {
2285-
/// Empty range (iteration has finished)
2284+
pub struct RangeInclusive<Idx> {
2285+
/// The lower bound of the range (inclusive).
22862286
#[unstable(feature = "inclusive_range",
22872287
reason = "recently added, follows RFC",
22882288
issue = "28237")]
2289-
Empty {
2290-
/// The point at which iteration finished
2291-
#[unstable(feature = "inclusive_range",
2292-
reason = "recently added, follows RFC",
2293-
issue = "28237")]
2294-
at: Idx
2295-
},
2296-
/// Non-empty range (iteration will yield value(s))
2289+
pub start: Idx,
2290+
/// The upper bound of the range (inclusive).
22972291
#[unstable(feature = "inclusive_range",
22982292
reason = "recently added, follows RFC",
22992293
issue = "28237")]
2300-
NonEmpty {
2301-
/// The lower bound of the range (inclusive).
2302-
#[unstable(feature = "inclusive_range",
2303-
reason = "recently added, follows RFC",
2304-
issue = "28237")]
2305-
start: Idx,
2306-
/// The upper bound of the range (inclusive).
2307-
#[unstable(feature = "inclusive_range",
2308-
reason = "recently added, follows RFC",
2309-
issue = "28237")]
2310-
end: Idx,
2311-
},
2294+
pub end: Idx,
23122295
}
23132296

23142297
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
23152298
impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
23162299
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
2317-
use self::RangeInclusive::*;
2318-
2319-
match *self {
2320-
Empty { ref at } => write!(fmt, "[empty range @ {:?}]", at),
2321-
NonEmpty { ref start, ref end } => write!(fmt, "{:?}...{:?}", start, end),
2322-
}
2300+
write!(fmt, "{:?}...{:?}", self.start, self.end)
23232301
}
23242302
}
23252303

@@ -2341,9 +2319,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
23412319
/// }
23422320
/// ```
23432321
pub fn contains(&self, item: Idx) -> bool {
2344-
if let &RangeInclusive::NonEmpty{ref start, ref end} = self {
2345-
(*start <= item) && (item <= *end)
2346-
} else { false }
2322+
self.start <= item && item <= self.end
23472323
}
23482324
}
23492325

0 commit comments

Comments
 (0)