Skip to content

Commit f50183d

Browse files
committed
Rollup merge of rust-lang#48087 - scottmcm:range_is_empty, r=kennytm,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~~
2 parents c268e6f + 22b0489 commit f50183d

File tree

5 files changed

+154
-14
lines changed

5 files changed

+154
-14
lines changed

src/libcore/iter/traits.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ pub trait ExactSizeIterator: Iterator {
706706
/// ```
707707
/// #![feature(exact_size_is_empty)]
708708
///
709-
/// let mut one_element = 0..1;
709+
/// let mut one_element = std::iter::once(0);
710710
/// assert!(!one_element.is_empty());
711711
///
712712
/// assert_eq!(one_element.next(), Some(0));

src/libcore/ops/range.rs

+76-4
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl fmt::Debug for RangeFull {
6060
/// (`start..end`).
6161
///
6262
/// The `Range` `start..end` contains all values with `x >= start` and
63-
/// `x < end`.
63+
/// `x < end`. It is empty unless `start < end`.
6464
///
6565
/// # Examples
6666
///
@@ -92,7 +92,6 @@ impl<Idx: fmt::Debug> fmt::Debug for Range<Idx> {
9292
}
9393
}
9494

95-
#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
9695
impl<Idx: PartialOrd<Idx>> Range<Idx> {
9796
/// Returns `true` if `item` is contained in the range.
9897
///
@@ -109,9 +108,37 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
109108
/// assert!(!(3..3).contains(3));
110109
/// assert!(!(3..2).contains(3));
111110
/// ```
111+
#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
112112
pub fn contains(&self, item: Idx) -> bool {
113113
(self.start <= item) && (item < self.end)
114114
}
115+
116+
/// Returns `true` if the range contains no items.
117+
///
118+
/// # Examples
119+
///
120+
/// ```
121+
/// #![feature(range_is_empty)]
122+
///
123+
/// assert!(!(3..5).is_empty());
124+
/// assert!( (3..3).is_empty());
125+
/// assert!( (3..2).is_empty());
126+
/// ```
127+
///
128+
/// The range is empty if either side is incomparable:
129+
///
130+
/// ```
131+
/// #![feature(range_is_empty,inclusive_range_syntax)]
132+
///
133+
/// use std::f32::NAN;
134+
/// assert!(!(3.0..5.0).is_empty());
135+
/// assert!( (3.0..NAN).is_empty());
136+
/// assert!( (NAN..5.0).is_empty());
137+
/// ```
138+
#[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
139+
pub fn is_empty(&self) -> bool {
140+
!(self.start < self.end)
141+
}
115142
}
116143

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

283-
#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
284317
impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
285318
/// Returns `true` if `item` is contained in the range.
286319
///
@@ -298,9 +331,48 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
298331
/// assert!( (3..=3).contains(3));
299332
/// assert!(!(3..=2).contains(3));
300333
/// ```
334+
#[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
301335
pub fn contains(&self, item: Idx) -> bool {
302336
self.start <= item && item <= self.end
303337
}
338+
339+
/// Returns `true` if the range contains no items.
340+
///
341+
/// # Examples
342+
///
343+
/// ```
344+
/// #![feature(range_is_empty,inclusive_range_syntax)]
345+
///
346+
/// assert!(!(3..=5).is_empty());
347+
/// assert!(!(3..=3).is_empty());
348+
/// assert!( (3..=2).is_empty());
349+
/// ```
350+
///
351+
/// The range is empty if either side is incomparable:
352+
///
353+
/// ```
354+
/// #![feature(range_is_empty,inclusive_range_syntax)]
355+
///
356+
/// use std::f32::NAN;
357+
/// assert!(!(3.0..=5.0).is_empty());
358+
/// assert!( (3.0..=NAN).is_empty());
359+
/// assert!( (NAN..=5.0).is_empty());
360+
/// ```
361+
///
362+
/// This method returns `true` after iteration has finished:
363+
///
364+
/// ```
365+
/// #![feature(range_is_empty,inclusive_range_syntax)]
366+
///
367+
/// let mut r = 3..=5;
368+
/// for _ in r.by_ref() {}
369+
/// // Precise field values are unspecified here
370+
/// assert!(r.is_empty());
371+
/// ```
372+
#[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
373+
pub fn is_empty(&self) -> bool {
374+
!(self.start <= self.end)
375+
}
304376
}
305377

306378
/// A range only bounded inclusively above (`..=end`).

src/libcore/tests/iter.rs

+52-9
Original file line numberDiff line numberDiff line change
@@ -1322,42 +1322,84 @@ fn test_range() {
13221322
(isize::MAX as usize + 2, Some(isize::MAX as usize + 2)));
13231323
}
13241324

1325+
#[test]
1326+
fn test_range_exhaustion() {
1327+
let mut r = 10..10;
1328+
assert!(r.is_empty());
1329+
assert_eq!(r.next(), None);
1330+
assert_eq!(r.next_back(), None);
1331+
assert_eq!(r, 10..10);
1332+
1333+
let mut r = 10..12;
1334+
assert_eq!(r.next(), Some(10));
1335+
assert_eq!(r.next(), Some(11));
1336+
assert!(r.is_empty());
1337+
assert_eq!(r, 12..12);
1338+
assert_eq!(r.next(), None);
1339+
1340+
let mut r = 10..12;
1341+
assert_eq!(r.next_back(), Some(11));
1342+
assert_eq!(r.next_back(), Some(10));
1343+
assert!(r.is_empty());
1344+
assert_eq!(r, 10..10);
1345+
assert_eq!(r.next_back(), None);
1346+
1347+
let mut r = 100..10;
1348+
assert!(r.is_empty());
1349+
assert_eq!(r.next(), None);
1350+
assert_eq!(r.next_back(), None);
1351+
assert_eq!(r, 100..10);
1352+
}
1353+
13251354
#[test]
13261355
fn test_range_inclusive_exhaustion() {
13271356
let mut r = 10..=10;
13281357
assert_eq!(r.next(), Some(10));
1329-
assert_eq!(r, 1..=0);
1358+
assert!(r.is_empty());
1359+
assert_eq!(r.next(), None);
1360+
assert_eq!(r.next(), None);
13301361

13311362
let mut r = 10..=10;
13321363
assert_eq!(r.next_back(), Some(10));
1333-
assert_eq!(r, 1..=0);
1364+
assert!(r.is_empty());
1365+
assert_eq!(r.next_back(), None);
13341366

13351367
let mut r = 10..=12;
13361368
assert_eq!(r.next(), Some(10));
13371369
assert_eq!(r.next(), Some(11));
13381370
assert_eq!(r.next(), Some(12));
1339-
assert_eq!(r, 1..=0);
1371+
assert!(r.is_empty());
1372+
assert_eq!(r.next(), None);
13401373

13411374
let mut r = 10..=12;
13421375
assert_eq!(r.next_back(), Some(12));
13431376
assert_eq!(r.next_back(), Some(11));
13441377
assert_eq!(r.next_back(), Some(10));
1345-
assert_eq!(r, 1..=0);
1378+
assert!(r.is_empty());
1379+
assert_eq!(r.next_back(), None);
13461380

13471381
let mut r = 10..=12;
13481382
assert_eq!(r.nth(2), Some(12));
1349-
assert_eq!(r, 1..=0);
1383+
assert!(r.is_empty());
1384+
assert_eq!(r.next(), None);
13501385

13511386
let mut r = 10..=12;
13521387
assert_eq!(r.nth(5), None);
1353-
assert_eq!(r, 1..=0);
1388+
assert!(r.is_empty());
1389+
assert_eq!(r.next(), None);
13541390

13551391
let mut r = 100..=10;
13561392
assert_eq!(r.next(), None);
1393+
assert!(r.is_empty());
1394+
assert_eq!(r.next(), None);
1395+
assert_eq!(r.next(), None);
13571396
assert_eq!(r, 100..=10);
13581397

13591398
let mut r = 100..=10;
13601399
assert_eq!(r.next_back(), None);
1400+
assert!(r.is_empty());
1401+
assert_eq!(r.next_back(), None);
1402+
assert_eq!(r.next_back(), None);
13611403
assert_eq!(r, 100..=10);
13621404
}
13631405

@@ -1428,9 +1470,10 @@ fn test_range_inclusive_nth() {
14281470
assert_eq!(r.nth(2), Some(15));
14291471
assert_eq!(r, 16..=20);
14301472
assert_eq!(r.is_empty(), false);
1473+
assert_eq!(ExactSizeIterator::is_empty(&r), false);
14311474
assert_eq!(r.nth(10), None);
14321475
assert_eq!(r.is_empty(), true);
1433-
assert_eq!(r, 1..=0); // We may not want to document/promise this detail
1476+
assert_eq!(ExactSizeIterator::is_empty(&r), true);
14341477
}
14351478

14361479
#[test]
@@ -1514,11 +1557,11 @@ fn test_range_inclusive_folds() {
15141557

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

15191562
let mut it = 10..=20;
15201563
assert_eq!(it.try_rfold(0, |a,b| Some(a+b)), Some(165));
1521-
assert_eq!(it, 1..=0);
1564+
assert!(it.is_empty());
15221565
}
15231566

15241567
#[test]

src/libcore/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#![feature(iter_rfold)]
3030
#![feature(nonzero)]
3131
#![feature(pattern)]
32+
#![feature(range_is_empty)]
3233
#![feature(raw)]
3334
#![feature(refcell_replace_swap)]
3435
#![feature(sip_hash_13)]

src/libcore/tests/ops.rs

+24
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,27 @@ fn test_range_inclusive() {
6868
assert_eq!(r.size_hint(), (0, Some(0)));
6969
assert_eq!(r.next(), None);
7070
}
71+
72+
73+
#[test]
74+
fn test_range_is_empty() {
75+
use core::f32::*;
76+
77+
assert!(!(0.0 .. 10.0).is_empty());
78+
assert!( (-0.0 .. 0.0).is_empty());
79+
assert!( (10.0 .. 0.0).is_empty());
80+
81+
assert!(!(NEG_INFINITY .. INFINITY).is_empty());
82+
assert!( (EPSILON .. NAN).is_empty());
83+
assert!( (NAN .. EPSILON).is_empty());
84+
assert!( (NAN .. NAN).is_empty());
85+
86+
assert!(!(0.0 ..= 10.0).is_empty());
87+
assert!(!(-0.0 ..= 0.0).is_empty());
88+
assert!( (10.0 ..= 0.0).is_empty());
89+
90+
assert!(!(NEG_INFINITY ..= INFINITY).is_empty());
91+
assert!( (EPSILON ..= NAN).is_empty());
92+
assert!( (NAN ..= EPSILON).is_empty());
93+
assert!( (NAN ..= NAN).is_empty());
94+
}

0 commit comments

Comments
 (0)