Skip to content

Commit

Permalink
Rollup merge of rust-lang#48087 - scottmcm:range_is_empty, r=kennytm,…
Browse files Browse the repository at this point in the history
…alexcrichton

Add Range[Inclusive]::is_empty

During rust-lang/rfcs#1980, it was discussed that figuring out whether a range is empty was subtle, and thus there should be a clear and obvious way to do it.  It can't just be ExactSizeIterator::is_empty (also unstable) because not all ranges are ExactSize -- such as `Range<i64>` and `RangeInclusive<usize>`.

Things to ponder:
- Unless this is stabilized first, this makes stabilizing ExactSizeIterator::is_empty more icky, since this hides that.
- This is only on `Range` and `RangeInclusive`, as those are the only ones where it's interesting.  But one could argue that it should be on more for consistency, or on RangeArgument instead.
- The bound on this is PartialOrd, since that works ok (see tests for float examples) and is consistent with `contains`.  But ranges like `NAN..=NAN`_are_ kinda weird.
- [x] ~~There's not a real issue number on this yet~~
  • Loading branch information
kennytm committed Feb 13, 2018
2 parents ab7257d + 22b0489 commit e44f2ae
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/libcore/iter/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ pub trait ExactSizeIterator: Iterator {
/// ```
/// #![feature(exact_size_is_empty)]
///
/// let mut one_element = 0..1;
/// let mut one_element = std::iter::once(0);
/// assert!(!one_element.is_empty());
///
/// assert_eq!(one_element.next(), Some(0));
Expand Down
80 changes: 76 additions & 4 deletions src/libcore/ops/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl fmt::Debug for RangeFull {
/// (`start..end`).
///
/// The `Range` `start..end` contains all values with `x >= start` and
/// `x < end`.
/// `x < end`. It is empty unless `start < end`.
///
/// # Examples
///
Expand Down Expand Up @@ -92,7 +92,6 @@ impl<Idx: fmt::Debug> fmt::Debug for Range<Idx> {
}
}

#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
impl<Idx: PartialOrd<Idx>> Range<Idx> {
/// Returns `true` if `item` is contained in the range.
///
Expand All @@ -109,9 +108,37 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
/// assert!(!(3..3).contains(3));
/// assert!(!(3..2).contains(3));
/// ```
#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
pub fn contains(&self, item: Idx) -> bool {
(self.start <= item) && (item < self.end)
}

/// Returns `true` if the range contains no items.
///
/// # Examples
///
/// ```
/// #![feature(range_is_empty)]
///
/// assert!(!(3..5).is_empty());
/// assert!( (3..3).is_empty());
/// assert!( (3..2).is_empty());
/// ```
///
/// The range is empty if either side is incomparable:
///
/// ```
/// #![feature(range_is_empty,inclusive_range_syntax)]
///
/// use std::f32::NAN;
/// assert!(!(3.0..5.0).is_empty());
/// assert!( (3.0..NAN).is_empty());
/// assert!( (NAN..5.0).is_empty());
/// ```
#[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
pub fn is_empty(&self) -> bool {
!(self.start < self.end)
}
}

/// A range only bounded inclusively below (`start..`).
Expand Down Expand Up @@ -244,7 +271,14 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
/// An range bounded inclusively below and above (`start..=end`).
///
/// The `RangeInclusive` `start..=end` contains all values with `x >= start`
/// and `x <= end`.
/// and `x <= end`. It is empty unless `start <= end`.
///
/// This iterator is [fused], but the specific values of `start` and `end` after
/// iteration has finished are **unspecified** other than that [`.is_empty()`]
/// will return `true` once no more values will be produced.
///
/// [fused]: ../iter/trait.FusedIterator.html
/// [`.is_empty()`]: #method.is_empty
///
/// # Examples
///
Expand Down Expand Up @@ -280,7 +314,6 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
}
}

#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// Returns `true` if `item` is contained in the range.
///
Expand All @@ -298,9 +331,48 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// assert!( (3..=3).contains(3));
/// assert!(!(3..=2).contains(3));
/// ```
#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
pub fn contains(&self, item: Idx) -> bool {
self.start <= item && item <= self.end
}

/// Returns `true` if the range contains no items.
///
/// # Examples
///
/// ```
/// #![feature(range_is_empty,inclusive_range_syntax)]
///
/// assert!(!(3..=5).is_empty());
/// assert!(!(3..=3).is_empty());
/// assert!( (3..=2).is_empty());
/// ```
///
/// The range is empty if either side is incomparable:
///
/// ```
/// #![feature(range_is_empty,inclusive_range_syntax)]
///
/// use std::f32::NAN;
/// assert!(!(3.0..=5.0).is_empty());
/// assert!( (3.0..=NAN).is_empty());
/// assert!( (NAN..=5.0).is_empty());
/// ```
///
/// This method returns `true` after iteration has finished:
///
/// ```
/// #![feature(range_is_empty,inclusive_range_syntax)]
///
/// let mut r = 3..=5;
/// for _ in r.by_ref() {}
/// // Precise field values are unspecified here
/// assert!(r.is_empty());
/// ```
#[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
pub fn is_empty(&self) -> bool {
!(self.start <= self.end)
}
}

/// A range only bounded inclusively above (`..=end`).
Expand Down
61 changes: 52 additions & 9 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1322,42 +1322,84 @@ fn test_range() {
(isize::MAX as usize + 2, Some(isize::MAX as usize + 2)));
}

#[test]
fn test_range_exhaustion() {
let mut r = 10..10;
assert!(r.is_empty());
assert_eq!(r.next(), None);
assert_eq!(r.next_back(), None);
assert_eq!(r, 10..10);

let mut r = 10..12;
assert_eq!(r.next(), Some(10));
assert_eq!(r.next(), Some(11));
assert!(r.is_empty());
assert_eq!(r, 12..12);
assert_eq!(r.next(), None);

let mut r = 10..12;
assert_eq!(r.next_back(), Some(11));
assert_eq!(r.next_back(), Some(10));
assert!(r.is_empty());
assert_eq!(r, 10..10);
assert_eq!(r.next_back(), None);

let mut r = 100..10;
assert!(r.is_empty());
assert_eq!(r.next(), None);
assert_eq!(r.next_back(), None);
assert_eq!(r, 100..10);
}

#[test]
fn test_range_inclusive_exhaustion() {
let mut r = 10..=10;
assert_eq!(r.next(), Some(10));
assert_eq!(r, 1..=0);
assert!(r.is_empty());
assert_eq!(r.next(), None);
assert_eq!(r.next(), None);

let mut r = 10..=10;
assert_eq!(r.next_back(), Some(10));
assert_eq!(r, 1..=0);
assert!(r.is_empty());
assert_eq!(r.next_back(), None);

let mut r = 10..=12;
assert_eq!(r.next(), Some(10));
assert_eq!(r.next(), Some(11));
assert_eq!(r.next(), Some(12));
assert_eq!(r, 1..=0);
assert!(r.is_empty());
assert_eq!(r.next(), None);

let mut r = 10..=12;
assert_eq!(r.next_back(), Some(12));
assert_eq!(r.next_back(), Some(11));
assert_eq!(r.next_back(), Some(10));
assert_eq!(r, 1..=0);
assert!(r.is_empty());
assert_eq!(r.next_back(), None);

let mut r = 10..=12;
assert_eq!(r.nth(2), Some(12));
assert_eq!(r, 1..=0);
assert!(r.is_empty());
assert_eq!(r.next(), None);

let mut r = 10..=12;
assert_eq!(r.nth(5), None);
assert_eq!(r, 1..=0);
assert!(r.is_empty());
assert_eq!(r.next(), None);

let mut r = 100..=10;
assert_eq!(r.next(), None);
assert!(r.is_empty());
assert_eq!(r.next(), None);
assert_eq!(r.next(), None);
assert_eq!(r, 100..=10);

let mut r = 100..=10;
assert_eq!(r.next_back(), None);
assert!(r.is_empty());
assert_eq!(r.next_back(), None);
assert_eq!(r.next_back(), None);
assert_eq!(r, 100..=10);
}

Expand Down Expand Up @@ -1428,9 +1470,10 @@ fn test_range_inclusive_nth() {
assert_eq!(r.nth(2), Some(15));
assert_eq!(r, 16..=20);
assert_eq!(r.is_empty(), false);
assert_eq!(ExactSizeIterator::is_empty(&r), false);
assert_eq!(r.nth(10), None);
assert_eq!(r.is_empty(), true);
assert_eq!(r, 1..=0); // We may not want to document/promise this detail
assert_eq!(ExactSizeIterator::is_empty(&r), true);
}

#[test]
Expand Down Expand Up @@ -1514,11 +1557,11 @@ fn test_range_inclusive_folds() {

let mut it = 10..=20;
assert_eq!(it.try_fold(0, |a,b| Some(a+b)), Some(165));
assert_eq!(it, 1..=0);
assert!(it.is_empty());

let mut it = 10..=20;
assert_eq!(it.try_rfold(0, |a,b| Some(a+b)), Some(165));
assert_eq!(it, 1..=0);
assert!(it.is_empty());
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#![feature(iter_rfold)]
#![feature(nonzero)]
#![feature(pattern)]
#![feature(range_is_empty)]
#![feature(raw)]
#![feature(refcell_replace_swap)]
#![feature(sip_hash_13)]
Expand Down
24 changes: 24 additions & 0 deletions src/libcore/tests/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,27 @@ fn test_range_inclusive() {
assert_eq!(r.size_hint(), (0, Some(0)));
assert_eq!(r.next(), None);
}


#[test]
fn test_range_is_empty() {
use core::f32::*;

assert!(!(0.0 .. 10.0).is_empty());
assert!( (-0.0 .. 0.0).is_empty());
assert!( (10.0 .. 0.0).is_empty());

assert!(!(NEG_INFINITY .. INFINITY).is_empty());
assert!( (EPSILON .. NAN).is_empty());
assert!( (NAN .. EPSILON).is_empty());
assert!( (NAN .. NAN).is_empty());

assert!(!(0.0 ..= 10.0).is_empty());
assert!(!(-0.0 ..= 0.0).is_empty());
assert!( (10.0 ..= 0.0).is_empty());

assert!(!(NEG_INFINITY ..= INFINITY).is_empty());
assert!( (EPSILON ..= NAN).is_empty());
assert!( (NAN ..= EPSILON).is_empty());
assert!( (NAN ..= NAN).is_empty());
}

0 comments on commit e44f2ae

Please sign in to comment.