about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2018-02-14 16:14:33 +0800
committerGitHub <noreply@github.com>2018-02-14 16:14:33 +0800
commitbd3674e4def219b22b3d5a3c3871569b0b153236 (patch)
tree025f5070b62192cd7039870141a720dfd67796fc /src/libcore
parent3715f1e490e187c3635ef241104bd5abe119958b (diff)
parent22b0489f80dae5242f19c4ce892b50d3685dbf82 (diff)
downloadrust-bd3674e4def219b22b3d5a3c3871569b0b153236.tar.gz
rust-bd3674e4def219b22b3d5a3c3871569b0b153236.zip
Rollup merge of #48087 - scottmcm:range_is_empty, r=kennytm,alexcrichton
Add Range[Inclusive]::is_empty

During https://github.com/rust-lang/rfcs/pull/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~~
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/iter/traits.rs2
-rw-r--r--src/libcore/ops/range.rs80
-rw-r--r--src/libcore/tests/iter.rs61
-rw-r--r--src/libcore/tests/lib.rs1
-rw-r--r--src/libcore/tests/ops.rs24
5 files changed, 154 insertions, 14 deletions
diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs
index be4889f2487..860742d9eab 100644
--- a/src/libcore/iter/traits.rs
+++ b/src/libcore/iter/traits.rs
@@ -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));
diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs
index 3f573f7c7eb..8a45444f1ab 100644
--- a/src/libcore/ops/range.rs
+++ b/src/libcore/ops/range.rs
@@ -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
 ///
@@ -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.
     ///
@@ -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..`).
@@ -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
 ///
@@ -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.
     ///
@@ -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`).
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index b2a5243d5e6..d8c9dcd8664 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -1323,41 +1323,83 @@ fn test_range() {
 }
 
 #[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);
 }
 
@@ -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]
@@ -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]
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index 9e90313bc0e..0d55e6eeeb1 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -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)]
diff --git a/src/libcore/tests/ops.rs b/src/libcore/tests/ops.rs
index 9d2fa1abff6..bed08f86d72 100644
--- a/src/libcore/tests/ops.rs
+++ b/src/libcore/tests/ops.rs
@@ -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());
+}