From 44e6e94335e347398e1d38deb6a601d15e1b1ce8 Mon Sep 17 00:00:00 2001 From: Pyry Kontio Date: Sat, 24 Sep 2022 04:15:39 +0900 Subject: [PATCH 1/2] New API: Range::cmp_scalar; comparison (less/equal/greater) to a primitive of the Range --- library/core/src/lib.rs | 1 + library/core/src/ops/range.rs | 136 +++++++++++++++++ library/core/tests/lib.rs | 1 + library/core/tests/ops.rs | 200 +------------------------ library/core/tests/ops/range.rs | 258 ++++++++++++++++++++++++++++++++ 5 files changed, 397 insertions(+), 199 deletions(-) create mode 100644 library/core/tests/ops/range.rs diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 21775c0a6ab0..cc67199db32b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -163,6 +163,7 @@ #![feature(const_slice_index)] #![feature(const_is_char_boundary)] #![feature(const_cstr_methods)] +#![feature(cmp_scalar)] // // Language features: #![feature(abi_unadjusted)] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 728202835818..921fb2819a84 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -147,6 +147,44 @@ impl> Range { } } +impl Range { + /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// if the range contains the scalar. If not, returns `Ordering::Less` + /// or `Ordering::Greater`, depending on whether the range is + /// below or above the scalar. + /// + /// # Examples + /// + /// ```rust + /// #![feature(cmp_scalar)] + /// # fn main() -> Result<(), usize> { + /// # struct File { + /// # seqnum: u32, + /// # range: core::ops::Range, + /// # } + /// let files = vec![ + /// File { seqnum: 0, range: 0..1000 }, + /// File { seqnum: 1, range: 1000..2200 }, + /// File { seqnum: 2, range: 2200..3900 }, + /// File { seqnum: 3, range: 3900..5000 }, + /// ]; + /// let target = 1600; + /// let index = files.binary_search_by(|f| f.range.cmp_scalar(target))?; + /// assert_eq!(files[index].seqnum, 1); + /// # Ok(()) } + /// ``` + #[unstable(feature = "cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { + if self.end <= scalar { + core::cmp::Ordering::Less + } else if scalar < self.start { + core::cmp::Ordering::Greater + } else { + core::cmp::Ordering::Equal + } + } +} + /// A range only bounded inclusively below (`start..`). /// /// The `RangeFrom` `start..` contains all values with `x >= start`. @@ -223,6 +261,26 @@ impl> RangeFrom { } } +impl RangeFrom { + /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// if the range contains the scalar. If not, the range + /// can be only above the scalar, so returns `Ordering::Greater`. + /// + /// # Examples + /// + /// ```rust + /// #![feature(cmp_scalar)] + /// # use core::cmp::Ordering; + /// assert_eq!((100..).cmp_scalar(50), Ordering::Greater); + /// assert_eq!((100..).cmp_scalar(100), Ordering::Equal); + /// assert_eq!((100..).cmp_scalar(150), Ordering::Equal); + /// ``` + #[unstable(feature = "cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { + if scalar < self.start { core::cmp::Ordering::Greater } else { core::cmp::Ordering::Equal } + } +} + /// A range only bounded exclusively above (`..end`). /// /// The `RangeTo` `..end` contains all values with `x < end`. @@ -304,6 +362,26 @@ impl> RangeTo { } } +impl RangeTo { + /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// if the range contains the scalar. If not, the range + /// can be only below the scalar, so returns `Ordering::Less`. + /// + /// # Examples + /// + /// ```rust + /// #![feature(cmp_scalar)] + /// # use core::cmp::Ordering; + /// assert_eq!((..100).cmp_scalar(50), Ordering::Equal); + /// assert_eq!((..100).cmp_scalar(100), Ordering::Less); + /// assert_eq!((..100).cmp_scalar(150), Ordering::Less); + /// ``` + #[unstable(feature = "cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { + if self.end <= scalar { core::cmp::Ordering::Less } else { core::cmp::Ordering::Equal } + } +} + /// A range bounded inclusively below and above (`start..=end`). /// /// The `RangeInclusive` `start..=end` contains all values with `x >= start` @@ -541,6 +619,44 @@ impl> RangeInclusive { } } +impl RangeInclusive { + /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// if the range contains the scalar. If not, returns `Ordering::Less` + /// or `Ordering::Greater`, depending on whether the range is + /// below or above the scalar. + /// + /// # Examples + /// + /// ```rust + /// #![feature(cmp_scalar)] + /// # fn main() -> Result<(), usize> { + /// # struct File { + /// # seqnum: u32, + /// # range: core::ops::RangeInclusive, + /// # } + /// let files = vec![ + /// File { seqnum: 0, range: 0..=999 }, + /// File { seqnum: 1, range: 1000..=2199 }, + /// File { seqnum: 2, range: 2200..=3899 }, + /// File { seqnum: 3, range: 3900..=4999 }, + /// ]; + /// let target = 1600; + /// let index = files.binary_search_by(|f| f.range.cmp_scalar(target))?; + /// assert_eq!(files[index].seqnum, 1); + /// # Ok(()) } + /// ``` + #[unstable(feature = "cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { + if *self.end() < scalar { + core::cmp::Ordering::Less + } else if scalar < *self.start() { + core::cmp::Ordering::Greater + } else { + core::cmp::Ordering::Equal + } + } +} + /// A range only bounded inclusively above (`..=end`). /// /// The `RangeToInclusive` `..=end` contains all values with `x <= end`. @@ -622,6 +738,26 @@ impl> RangeToInclusive { } } +impl RangeToInclusive { + /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// if the range contains the scalar. If not, the range + /// can be only below the scalar, so returns `Ordering::Less`. + /// + /// # Examples + /// + /// ```rust + /// #![feature(cmp_scalar)] + /// # use core::cmp::Ordering; + /// assert_eq!((..=100).cmp_scalar(50), Ordering::Equal); + /// assert_eq!((..=100).cmp_scalar(100), Ordering::Equal); + /// assert_eq!((..=100).cmp_scalar(150), Ordering::Less); + /// ``` + #[unstable(feature = "cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { + if self.end < scalar { core::cmp::Ordering::Less } else { core::cmp::Ordering::Equal } + } +} + // RangeToInclusive cannot impl From> // because underflow would be possible with (..0).into() diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 46f603eaebac..a59bb0dccb07 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -101,6 +101,7 @@ #![feature(slice_flatten)] #![feature(provide_any)] #![feature(utf8_chunks)] +#![feature(cmp_scalar)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test; diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs index 0c81cba35b3d..1cdf8efb0fff 100644 --- a/library/core/tests/ops.rs +++ b/library/core/tests/ops.rs @@ -1,206 +1,8 @@ mod control_flow; +mod range; -use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; use core::ops::{Deref, DerefMut}; -// Test the Range structs and syntax. - -#[test] -fn test_range() { - let r = Range { start: 2, end: 10 }; - let mut count = 0; - for (i, ri) in r.enumerate() { - assert_eq!(ri, i + 2); - assert!(ri >= 2 && ri < 10); - count += 1; - } - assert_eq!(count, 8); -} - -#[test] -fn test_range_from() { - let r = RangeFrom { start: 2 }; - let mut count = 0; - for (i, ri) in r.take(10).enumerate() { - assert_eq!(ri, i + 2); - assert!(ri >= 2 && ri < 12); - count += 1; - } - assert_eq!(count, 10); -} - -#[test] -fn test_range_to() { - // Not much to test. - let _ = RangeTo { end: 42 }; -} - -#[test] -fn test_full_range() { - // Not much to test. - let _ = RangeFull; -} - -#[test] -fn test_range_inclusive() { - let mut r = RangeInclusive::new(1i8, 2); - assert_eq!(r.next(), Some(1)); - assert_eq!(r.next(), Some(2)); - assert_eq!(r.next(), None); - - r = RangeInclusive::new(127i8, 127); - assert_eq!(r.next(), Some(127)); - assert_eq!(r.next(), None); - - r = RangeInclusive::new(-128i8, -128); - assert_eq!(r.next_back(), Some(-128)); - assert_eq!(r.next_back(), None); - - // degenerate - r = RangeInclusive::new(1, -1); - assert_eq!(r.size_hint(), (0, Some(0))); - assert_eq!(r.next(), None); -} - -#[test] -fn test_range_to_inclusive() { - // Not much to test. - let _ = RangeToInclusive { end: 42 }; -} - -#[test] -fn test_range_is_empty() { - assert!(!(0.0..10.0).is_empty()); - assert!((-0.0..0.0).is_empty()); - assert!((10.0..0.0).is_empty()); - - assert!(!(f32::NEG_INFINITY..f32::INFINITY).is_empty()); - assert!((f32::EPSILON..f32::NAN).is_empty()); - assert!((f32::NAN..f32::EPSILON).is_empty()); - assert!((f32::NAN..f32::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!(!(f32::NEG_INFINITY..=f32::INFINITY).is_empty()); - assert!((f32::EPSILON..=f32::NAN).is_empty()); - assert!((f32::NAN..=f32::EPSILON).is_empty()); - assert!((f32::NAN..=f32::NAN).is_empty()); -} - -#[test] -fn test_bound_cloned_unbounded() { - assert_eq!(Bound::<&u32>::Unbounded.cloned(), Bound::Unbounded); -} - -#[test] -fn test_bound_cloned_included() { - assert_eq!(Bound::Included(&3).cloned(), Bound::Included(3)); -} - -#[test] -fn test_bound_cloned_excluded() { - assert_eq!(Bound::Excluded(&3).cloned(), Bound::Excluded(3)); -} - -#[test] -#[allow(unused_comparisons)] -#[allow(unused_mut)] -fn test_range_syntax() { - let mut count = 0; - for i in 0_usize..10 { - assert!(i >= 0 && i < 10); - count += i; - } - assert_eq!(count, 45); - - let mut count = 0; - let mut range = 0_usize..10; - for i in range { - assert!(i >= 0 && i < 10); - count += i; - } - assert_eq!(count, 45); - - let mut count = 0; - let mut rf = 3_usize..; - for i in rf.take(10) { - assert!(i >= 3 && i < 13); - count += i; - } - assert_eq!(count, 75); - - let _ = 0_usize..4 + 4 - 3; - - fn foo() -> isize { - 42 - } - let _ = 0..foo(); - - let _ = { &42..&100 }; // references to literals are OK - let _ = ..42_usize; - - // Test we can use two different types with a common supertype. - let x = &42; - { - let y = 42; - let _ = x..&y; - } -} - -#[test] -#[allow(dead_code)] -fn test_range_syntax_in_return_statement() { - fn return_range_to() -> RangeTo { - return ..1; - } - fn return_full_range() -> RangeFull { - return ..; - } - // Not much to test. -} - -#[test] -fn range_structural_match() { - // test that all range types can be structurally matched upon - - const RANGE: Range = 0..1000; - match RANGE { - RANGE => {} - _ => unreachable!(), - } - - const RANGE_FROM: RangeFrom = 0..; - match RANGE_FROM { - RANGE_FROM => {} - _ => unreachable!(), - } - - const RANGE_FULL: RangeFull = ..; - match RANGE_FULL { - RANGE_FULL => {} - } - - const RANGE_INCLUSIVE: RangeInclusive = 0..=999; - match RANGE_INCLUSIVE { - RANGE_INCLUSIVE => {} - _ => unreachable!(), - } - - const RANGE_TO: RangeTo = ..1000; - match RANGE_TO { - RANGE_TO => {} - _ => unreachable!(), - } - - const RANGE_TO_INCLUSIVE: RangeToInclusive = ..=999; - match RANGE_TO_INCLUSIVE { - RANGE_TO_INCLUSIVE => {} - _ => unreachable!(), - } -} - // Test Deref implementations #[test] diff --git a/library/core/tests/ops/range.rs b/library/core/tests/ops/range.rs new file mode 100644 index 000000000000..075947137458 --- /dev/null +++ b/library/core/tests/ops/range.rs @@ -0,0 +1,258 @@ +use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; + +// Test the Range structs and syntax. + +#[test] +fn test_range() { + let r = Range { start: 2, end: 10 }; + let mut count = 0; + for (i, ri) in r.enumerate() { + assert_eq!(ri, i + 2); + assert!(ri >= 2 && ri < 10); + count += 1; + } + assert_eq!(count, 8); +} + +#[test] +fn test_range_from() { + let r = RangeFrom { start: 2 }; + let mut count = 0; + for (i, ri) in r.take(10).enumerate() { + assert_eq!(ri, i + 2); + assert!(ri >= 2 && ri < 12); + count += 1; + } + assert_eq!(count, 10); +} + +#[test] +fn test_range_to() { + // Not much to test. + let _ = RangeTo { end: 42 }; +} + +#[test] +fn test_full_range() { + // Not much to test. + let _ = RangeFull; +} + +#[test] +fn test_range_inclusive() { + let mut r = RangeInclusive::new(1i8, 2); + assert_eq!(r.next(), Some(1)); + assert_eq!(r.next(), Some(2)); + assert_eq!(r.next(), None); + + r = RangeInclusive::new(127i8, 127); + assert_eq!(r.next(), Some(127)); + assert_eq!(r.next(), None); + + r = RangeInclusive::new(-128i8, -128); + assert_eq!(r.next_back(), Some(-128)); + assert_eq!(r.next_back(), None); + + // degenerate + r = RangeInclusive::new(1, -1); + assert_eq!(r.size_hint(), (0, Some(0))); + assert_eq!(r.next(), None); +} + +#[test] +fn test_range_to_inclusive() { + // Not much to test. + let _ = RangeToInclusive { end: 42 }; +} + +#[test] +fn test_range_is_empty() { + assert!(!(0.0..10.0).is_empty()); + assert!((-0.0..0.0).is_empty()); + assert!((10.0..0.0).is_empty()); + + assert!(!(f32::NEG_INFINITY..f32::INFINITY).is_empty()); + assert!((f32::EPSILON..f32::NAN).is_empty()); + assert!((f32::NAN..f32::EPSILON).is_empty()); + assert!((f32::NAN..f32::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!(!(f32::NEG_INFINITY..=f32::INFINITY).is_empty()); + assert!((f32::EPSILON..=f32::NAN).is_empty()); + assert!((f32::NAN..=f32::EPSILON).is_empty()); + assert!((f32::NAN..=f32::NAN).is_empty()); +} + +#[test] +fn test_bound_cloned_unbounded() { + assert_eq!(Bound::<&u32>::Unbounded.cloned(), Bound::Unbounded); +} + +#[test] +fn test_bound_cloned_included() { + assert_eq!(Bound::Included(&3).cloned(), Bound::Included(3)); +} + +#[test] +fn test_bound_cloned_excluded() { + assert_eq!(Bound::Excluded(&3).cloned(), Bound::Excluded(3)); +} + +#[test] +#[allow(unused_comparisons)] +#[allow(unused_mut)] +fn test_range_syntax() { + let mut count = 0; + for i in 0_usize..10 { + assert!(i >= 0 && i < 10); + count += i; + } + assert_eq!(count, 45); + + let mut count = 0; + let mut range = 0_usize..10; + for i in range { + assert!(i >= 0 && i < 10); + count += i; + } + assert_eq!(count, 45); + + let mut count = 0; + let mut rf = 3_usize..; + for i in rf.take(10) { + assert!(i >= 3 && i < 13); + count += i; + } + assert_eq!(count, 75); + + let _ = 0_usize..4 + 4 - 3; + + fn foo() -> isize { + 42 + } + let _ = 0..foo(); + + let _ = { &42..&100 }; // references to literals are OK + let _ = ..42_usize; + + // Test we can use two different types with a common supertype. + let x = &42; + { + let y = 42; + let _ = x..&y; + } +} + +#[test] +#[allow(dead_code)] +fn test_range_syntax_in_return_statement() { + fn return_range_to() -> RangeTo { + return ..1; + } + fn return_full_range() -> RangeFull { + return ..; + } + // Not much to test. +} + +#[test] +fn range_structural_match() { + // test that all range types can be structurally matched upon + + const RANGE: Range = 0..1000; + match RANGE { + RANGE => {} + _ => unreachable!(), + } + + const RANGE_FROM: RangeFrom = 0..; + match RANGE_FROM { + RANGE_FROM => {} + _ => unreachable!(), + } + + const RANGE_FULL: RangeFull = ..; + match RANGE_FULL { + RANGE_FULL => {} + } + + const RANGE_INCLUSIVE: RangeInclusive = 0..=999; + match RANGE_INCLUSIVE { + RANGE_INCLUSIVE => {} + _ => unreachable!(), + } + + const RANGE_TO: RangeTo = ..1000; + match RANGE_TO { + RANGE_TO => {} + _ => unreachable!(), + } + + const RANGE_TO_INCLUSIVE: RangeToInclusive = ..=999; + match RANGE_TO_INCLUSIVE { + RANGE_TO_INCLUSIVE => {} + _ => unreachable!(), + } +} + +// Ranges are a nest of one-off errors so... +#[test] +fn test_cmp_range() { + use core::cmp::Ordering::{Equal, Greater, Less}; + + // Range + assert_eq!((50..150).cmp_scalar(40), Greater); + assert_eq!((50..150).cmp_scalar(49), Greater); + assert_eq!((50..150).cmp_scalar(50), Equal); + assert_eq!((50..150).cmp_scalar(51), Equal); + assert_eq!((50..150).cmp_scalar(100), Equal); + assert_eq!((50..150).cmp_scalar(149), Equal); + assert_eq!((50..150).cmp_scalar(150), Less); + assert_eq!((50..150).cmp_scalar(151), Less); + assert_eq!((50..150).cmp_scalar(160), Less); + + // RangeInclusive + assert_eq!((50..=150).cmp_scalar(40), Greater); + assert_eq!((50..=150).cmp_scalar(49), Greater); + assert_eq!((50..=150).cmp_scalar(50), Equal); + assert_eq!((50..=150).cmp_scalar(51), Equal); + assert_eq!((50..=150).cmp_scalar(100), Equal); + assert_eq!((50..=150).cmp_scalar(149), Equal); + assert_eq!((50..=150).cmp_scalar(150), Equal); + assert_eq!((50..=150).cmp_scalar(151), Less); + assert_eq!((50..=150).cmp_scalar(160), Less); + + // RangeFrom + assert_eq!((50..).cmp_scalar(49), Greater); + assert_eq!((50..).cmp_scalar(50), Equal); + assert_eq!((50..).cmp_scalar(51), Equal); + assert_eq!((50..).cmp_scalar(100), Equal); + assert_eq!((50..).cmp_scalar(u32::MAX), Equal); + + // RangeTo + assert_eq!((..150).cmp_scalar(u32::MIN), Equal); + assert_eq!((..150).cmp_scalar(149), Equal); + assert_eq!((..150).cmp_scalar(150), Less); + assert_eq!((..150).cmp_scalar(151), Less); + assert_eq!((..150).cmp_scalar(160), Less); + + // RangeToInclusive + assert_eq!((..=150).cmp_scalar(u32::MIN), Equal); + assert_eq!((..=150).cmp_scalar(149), Equal); + assert_eq!((..=150).cmp_scalar(150), Equal); + assert_eq!((..=150).cmp_scalar(151), Less); + assert_eq!((..=150).cmp_scalar(160), Less); + + // Empty ranges + assert_eq!((50..50).cmp_scalar(49), Greater); + assert_eq!((50..50).cmp_scalar(50), Less); + assert_eq!((50..50).cmp_scalar(51), Less); + + // Almost-empty inclusive ranges + assert_eq!((50..=50).cmp_scalar(49), Greater); + assert_eq!((50..=50).cmp_scalar(50), Equal); + assert_eq!((50..=50).cmp_scalar(51), Less); +} From c803cd6d6006c6cdb3caf9e8a0a84cc7f5008bce Mon Sep 17 00:00:00 2001 From: Pyry Kontio Date: Thu, 29 Sep 2022 23:22:53 +0900 Subject: [PATCH 2/2] Addressed comments: the return values are now Option to address degenerate ranges. Added simple examples to docs. --- library/core/src/lib.rs | 2 +- library/core/src/ops/range.rs | 173 +++++++++++++++++++++----------- library/core/tests/lib.rs | 2 +- library/core/tests/ops/range.rs | 97 +++++++++++------- 4 files changed, 172 insertions(+), 102 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index cc67199db32b..222c5a498be0 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -163,7 +163,7 @@ #![feature(const_slice_index)] #![feature(const_is_char_boundary)] #![feature(const_cstr_methods)] -#![feature(cmp_scalar)] +#![feature(range_cmp_scalar)] // // Language features: #![feature(abi_unadjusted)] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 921fb2819a84..09128f804c9b 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -145,18 +145,32 @@ impl> Range { pub fn is_empty(&self) -> bool { !(self.start < self.end) } -} -impl Range { - /// Compares the range to a scalar. Returns `Ordering::Equal`, - /// if the range contains the scalar. If not, returns `Ordering::Less` - /// or `Ordering::Greater`, depending on whether the range is - /// below or above the scalar. + /// Compares the range to a scalar. Returns `Some(Ordering::Equal)`, + /// if the range contains the scalar. If not, returns `Some(Ordering::Less)` + /// or `Some(Ordering::Greater)`, depending on whether the range is + /// below or above the scalar. Returns `None` if the values are + /// uncomparable (for example, NaN) or if the range is degenerate + /// (i.e. end < start). /// /// # Examples /// /// ```rust - /// #![feature(cmp_scalar)] + /// #![feature(range_cmp_scalar)] + /// # use core::cmp::Ordering; + /// assert_eq!((1..10).cmp_scalar(-1234), Some(Ordering::Greater)); + /// assert_eq!((1..10).cmp_scalar(0), Some(Ordering::Greater)); + /// assert_eq!((1..10).cmp_scalar(1), Some(Ordering::Equal)); + /// assert_eq!((1..10).cmp_scalar(5), Some(Ordering::Equal)); + /// assert_eq!((1..10).cmp_scalar(9), Some(Ordering::Equal)); + /// assert_eq!((1..10).cmp_scalar(10), Some(Ordering::Less)); + /// assert_eq!((1..10).cmp_scalar(1234), Some(Ordering::Less)); + /// assert_eq!((20..10).cmp_scalar(15), None); + /// assert_eq!((f32::NAN..f32::NAN).cmp_scalar(10.0), None); + /// ``` + /// + /// ```rust + /// #![feature(range_cmp_scalar)] /// # fn main() -> Result<(), usize> { /// # struct File { /// # seqnum: u32, @@ -169,18 +183,22 @@ impl Range { /// File { seqnum: 3, range: 3900..5000 }, /// ]; /// let target = 1600; - /// let index = files.binary_search_by(|f| f.range.cmp_scalar(target))?; + /// let index = files.binary_search_by(|f| f.range.cmp_scalar(target).unwrap())?; /// assert_eq!(files[index].seqnum, 1); /// # Ok(()) } /// ``` - #[unstable(feature = "cmp_scalar", issue = "none")] - pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { - if self.end <= scalar { - core::cmp::Ordering::Less + #[unstable(feature = "range_cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> Option { + if self.end < self.start { + None + } else if self.end <= scalar { + Some(core::cmp::Ordering::Less) } else if scalar < self.start { - core::cmp::Ordering::Greater + Some(core::cmp::Ordering::Greater) + } else if self.start <= scalar && scalar < self.end { + Some(core::cmp::Ordering::Equal) } else { - core::cmp::Ordering::Equal + None } } } @@ -259,25 +277,30 @@ impl> RangeFrom { { >::contains(self, item) } -} -impl RangeFrom { - /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// Compares the range to a scalar. Returns `Some(Ordering::Equal)`, /// if the range contains the scalar. If not, the range - /// can be only above the scalar, so returns `Ordering::Greater`. + /// can be only above the scalar, so returns `Some(Ordering::Greater)`. + /// Returns `None` if the values are uncomparable (for example, NaN). /// /// # Examples /// /// ```rust - /// #![feature(cmp_scalar)] + /// #![feature(range_cmp_scalar)] /// # use core::cmp::Ordering; - /// assert_eq!((100..).cmp_scalar(50), Ordering::Greater); - /// assert_eq!((100..).cmp_scalar(100), Ordering::Equal); - /// assert_eq!((100..).cmp_scalar(150), Ordering::Equal); + /// assert_eq!((100..).cmp_scalar(50), Some(Ordering::Greater)); + /// assert_eq!((100..).cmp_scalar(100), Some(Ordering::Equal)); + /// assert_eq!((100..).cmp_scalar(150), Some(Ordering::Equal)); /// ``` - #[unstable(feature = "cmp_scalar", issue = "none")] - pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { - if scalar < self.start { core::cmp::Ordering::Greater } else { core::cmp::Ordering::Equal } + #[unstable(feature = "range_cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> Option { + if scalar < self.start { + Some(core::cmp::Ordering::Greater) + } else if self.start <= scalar { + Some(core::cmp::Ordering::Equal) + } else { + None + } } } @@ -360,25 +383,30 @@ impl> RangeTo { { >::contains(self, item) } -} -impl RangeTo { - /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// Compares the range to a scalar. Returns `Some(Ordering::Equal)`, /// if the range contains the scalar. If not, the range - /// can be only below the scalar, so returns `Ordering::Less`. + /// can be only below the scalar, so returns `Some(Ordering::Less)`. + /// Returns `None` if the values are uncomparable (for example, NaN). /// /// # Examples /// /// ```rust - /// #![feature(cmp_scalar)] + /// #![feature(range_cmp_scalar)] /// # use core::cmp::Ordering; - /// assert_eq!((..100).cmp_scalar(50), Ordering::Equal); - /// assert_eq!((..100).cmp_scalar(100), Ordering::Less); - /// assert_eq!((..100).cmp_scalar(150), Ordering::Less); + /// assert_eq!((..100).cmp_scalar(50), Some(Ordering::Equal)); + /// assert_eq!((..100).cmp_scalar(100), Some(Ordering::Less)); + /// assert_eq!((..100).cmp_scalar(150), Some(Ordering::Less)); /// ``` - #[unstable(feature = "cmp_scalar", issue = "none")] - pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { - if self.end <= scalar { core::cmp::Ordering::Less } else { core::cmp::Ordering::Equal } + #[unstable(feature = "range_cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> Option { + if self.end <= scalar { + Some(core::cmp::Ordering::Less) + } else if scalar < self.end { + Some(core::cmp::Ordering::Equal) + } else { + None + } } } @@ -617,18 +645,32 @@ impl> RangeInclusive { pub fn is_empty(&self) -> bool { self.exhausted || !(self.start <= self.end) } -} -impl RangeInclusive { - /// Compares the range to a scalar. Returns `Ordering::Equal`, - /// if the range contains the scalar. If not, returns `Ordering::Less` - /// or `Ordering::Greater`, depending on whether the range is - /// below or above the scalar. + /// Compares the range to a scalar. Returns `Some(Ordering::Equal)`, + /// if the range contains the scalar. If not, returns `Some(Ordering::Less)` + /// or `Some(Ordering::Greater)`, depending on whether the range is + /// below or above the scalar. Returns `None` if the values are + /// uncomparable (for example, NaN) or if the range is degenerate + /// (i.e. end < start). /// /// # Examples /// /// ```rust - /// #![feature(cmp_scalar)] + /// #![feature(range_cmp_scalar)] + /// # use core::cmp::Ordering; + /// assert_eq!((1..=10).cmp_scalar(-1234), Some(Ordering::Greater)); + /// assert_eq!((1..=10).cmp_scalar(0), Some(Ordering::Greater)); + /// assert_eq!((1..=10).cmp_scalar(1), Some(Ordering::Equal)); + /// assert_eq!((1..=10).cmp_scalar(5), Some(Ordering::Equal)); + /// assert_eq!((1..=10).cmp_scalar(9), Some(Ordering::Equal)); + /// assert_eq!((1..=10).cmp_scalar(10), Some(Ordering::Equal)); + /// assert_eq!((1..=10).cmp_scalar(1234), Some(Ordering::Less)); + /// assert_eq!((20..=10).cmp_scalar(15), None); + /// assert_eq!((f32::NAN..=f32::NAN).cmp_scalar(10.0), None); + /// ``` + /// + /// ```rust + /// #![feature(range_cmp_scalar)] /// # fn main() -> Result<(), usize> { /// # struct File { /// # seqnum: u32, @@ -641,18 +683,22 @@ impl RangeInclusive { /// File { seqnum: 3, range: 3900..=4999 }, /// ]; /// let target = 1600; - /// let index = files.binary_search_by(|f| f.range.cmp_scalar(target))?; + /// let index = files.binary_search_by(|f| f.range.cmp_scalar(target).unwrap())?; /// assert_eq!(files[index].seqnum, 1); /// # Ok(()) } /// ``` - #[unstable(feature = "cmp_scalar", issue = "none")] - pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { - if *self.end() < scalar { - core::cmp::Ordering::Less + #[unstable(feature = "range_cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> Option { + if *self.end() < *self.start() { + None + } else if *self.end() < scalar { + Some(core::cmp::Ordering::Less) } else if scalar < *self.start() { - core::cmp::Ordering::Greater + Some(core::cmp::Ordering::Greater) + } else if *self.start() <= scalar && scalar <= *self.end() { + Some(core::cmp::Ordering::Equal) } else { - core::cmp::Ordering::Equal + None } } } @@ -736,25 +782,30 @@ impl> RangeToInclusive { { >::contains(self, item) } -} -impl RangeToInclusive { - /// Compares the range to a scalar. Returns `Ordering::Equal`, + /// Compares the range to a scalar. Returns `Some(Ordering::Equal)`, /// if the range contains the scalar. If not, the range - /// can be only below the scalar, so returns `Ordering::Less`. + /// can be only below the scalar, so returns `Some(Ordering::Less)`. + /// Returns `None` if the values are uncomparable (for example, NaN). /// /// # Examples /// /// ```rust - /// #![feature(cmp_scalar)] + /// #![feature(range_cmp_scalar)] /// # use core::cmp::Ordering; - /// assert_eq!((..=100).cmp_scalar(50), Ordering::Equal); - /// assert_eq!((..=100).cmp_scalar(100), Ordering::Equal); - /// assert_eq!((..=100).cmp_scalar(150), Ordering::Less); + /// assert_eq!((..=100).cmp_scalar(50), Some(Ordering::Equal)); + /// assert_eq!((..=100).cmp_scalar(100), Some(Ordering::Equal)); + /// assert_eq!((..=100).cmp_scalar(150), Some(Ordering::Less)); /// ``` - #[unstable(feature = "cmp_scalar", issue = "none")] - pub fn cmp_scalar(&self, scalar: Idx) -> core::cmp::Ordering { - if self.end < scalar { core::cmp::Ordering::Less } else { core::cmp::Ordering::Equal } + #[unstable(feature = "range_cmp_scalar", issue = "none")] + pub fn cmp_scalar(&self, scalar: Idx) -> Option { + if self.end < scalar { + Some(core::cmp::Ordering::Less) + } else if scalar <= self.end { + Some(core::cmp::Ordering::Equal) + } else { + None + } } } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index a59bb0dccb07..a554c50b5124 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -101,7 +101,7 @@ #![feature(slice_flatten)] #![feature(provide_any)] #![feature(utf8_chunks)] -#![feature(cmp_scalar)] +#![feature(range_cmp_scalar)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test; diff --git a/library/core/tests/ops/range.rs b/library/core/tests/ops/range.rs index 075947137458..a99587b04a67 100644 --- a/library/core/tests/ops/range.rs +++ b/library/core/tests/ops/range.rs @@ -204,55 +204,74 @@ fn test_cmp_range() { use core::cmp::Ordering::{Equal, Greater, Less}; // Range - assert_eq!((50..150).cmp_scalar(40), Greater); - assert_eq!((50..150).cmp_scalar(49), Greater); - assert_eq!((50..150).cmp_scalar(50), Equal); - assert_eq!((50..150).cmp_scalar(51), Equal); - assert_eq!((50..150).cmp_scalar(100), Equal); - assert_eq!((50..150).cmp_scalar(149), Equal); - assert_eq!((50..150).cmp_scalar(150), Less); - assert_eq!((50..150).cmp_scalar(151), Less); - assert_eq!((50..150).cmp_scalar(160), Less); + assert_eq!((50..150).cmp_scalar(40), Some(Greater)); + assert_eq!((50..150).cmp_scalar(49), Some(Greater)); + assert_eq!((50..150).cmp_scalar(50), Some(Equal)); + assert_eq!((50..150).cmp_scalar(51), Some(Equal)); + assert_eq!((50..150).cmp_scalar(100), Some(Equal)); + assert_eq!((50..150).cmp_scalar(149), Some(Equal)); + assert_eq!((50..150).cmp_scalar(150), Some(Less)); + assert_eq!((50..150).cmp_scalar(151), Some(Less)); + assert_eq!((50..150).cmp_scalar(160), Some(Less)); // RangeInclusive - assert_eq!((50..=150).cmp_scalar(40), Greater); - assert_eq!((50..=150).cmp_scalar(49), Greater); - assert_eq!((50..=150).cmp_scalar(50), Equal); - assert_eq!((50..=150).cmp_scalar(51), Equal); - assert_eq!((50..=150).cmp_scalar(100), Equal); - assert_eq!((50..=150).cmp_scalar(149), Equal); - assert_eq!((50..=150).cmp_scalar(150), Equal); - assert_eq!((50..=150).cmp_scalar(151), Less); - assert_eq!((50..=150).cmp_scalar(160), Less); + assert_eq!((50..=150).cmp_scalar(40), Some(Greater)); + assert_eq!((50..=150).cmp_scalar(49), Some(Greater)); + assert_eq!((50..=150).cmp_scalar(50), Some(Equal)); + assert_eq!((50..=150).cmp_scalar(51), Some(Equal)); + assert_eq!((50..=150).cmp_scalar(100), Some(Equal)); + assert_eq!((50..=150).cmp_scalar(149), Some(Equal)); + assert_eq!((50..=150).cmp_scalar(150), Some(Equal)); + assert_eq!((50..=150).cmp_scalar(151), Some(Less)); + assert_eq!((50..=150).cmp_scalar(160), Some(Less)); // RangeFrom - assert_eq!((50..).cmp_scalar(49), Greater); - assert_eq!((50..).cmp_scalar(50), Equal); - assert_eq!((50..).cmp_scalar(51), Equal); - assert_eq!((50..).cmp_scalar(100), Equal); - assert_eq!((50..).cmp_scalar(u32::MAX), Equal); + assert_eq!((50..).cmp_scalar(49), Some(Greater)); + assert_eq!((50..).cmp_scalar(50), Some(Equal)); + assert_eq!((50..).cmp_scalar(51), Some(Equal)); + assert_eq!((50..).cmp_scalar(100), Some(Equal)); + assert_eq!((50..).cmp_scalar(u32::MAX), Some(Equal)); // RangeTo - assert_eq!((..150).cmp_scalar(u32::MIN), Equal); - assert_eq!((..150).cmp_scalar(149), Equal); - assert_eq!((..150).cmp_scalar(150), Less); - assert_eq!((..150).cmp_scalar(151), Less); - assert_eq!((..150).cmp_scalar(160), Less); + assert_eq!((..150).cmp_scalar(u32::MIN), Some(Equal)); + assert_eq!((..150).cmp_scalar(149), Some(Equal)); + assert_eq!((..150).cmp_scalar(150), Some(Less)); + assert_eq!((..150).cmp_scalar(151), Some(Less)); + assert_eq!((..150).cmp_scalar(160), Some(Less)); // RangeToInclusive - assert_eq!((..=150).cmp_scalar(u32::MIN), Equal); - assert_eq!((..=150).cmp_scalar(149), Equal); - assert_eq!((..=150).cmp_scalar(150), Equal); - assert_eq!((..=150).cmp_scalar(151), Less); - assert_eq!((..=150).cmp_scalar(160), Less); + assert_eq!((..=150).cmp_scalar(u32::MIN), Some(Equal)); + assert_eq!((..=150).cmp_scalar(149), Some(Equal)); + assert_eq!((..=150).cmp_scalar(150), Some(Equal)); + assert_eq!((..=150).cmp_scalar(151), Some(Less)); + assert_eq!((..=150).cmp_scalar(160), Some(Less)); // Empty ranges - assert_eq!((50..50).cmp_scalar(49), Greater); - assert_eq!((50..50).cmp_scalar(50), Less); - assert_eq!((50..50).cmp_scalar(51), Less); + assert_eq!((50..50).cmp_scalar(49), Some(Greater)); + assert_eq!((50..50).cmp_scalar(50), Some(Less)); + assert_eq!((50..50).cmp_scalar(51), Some(Less)); // Almost-empty inclusive ranges - assert_eq!((50..=50).cmp_scalar(49), Greater); - assert_eq!((50..=50).cmp_scalar(50), Equal); - assert_eq!((50..=50).cmp_scalar(51), Less); + assert_eq!((50..=50).cmp_scalar(49), Some(Greater)); + assert_eq!((50..=50).cmp_scalar(50), Some(Equal)); + assert_eq!((50..=50).cmp_scalar(51), Some(Less)); + + // Degenerate ranges + assert_eq!((20..10).cmp_scalar(15), None); + assert_eq!((20..=10).cmp_scalar(15), None); + + // Uncomparables + assert_eq!((10.0..20.0).cmp_scalar(f32::NAN), None); + assert_eq!((10.0..=20.0).cmp_scalar(f32::NAN), None); + assert_eq!((10.0..).cmp_scalar(f32::NAN), None); + assert_eq!((..20.0).cmp_scalar(f32::NAN), None); + assert_eq!((..=20.0).cmp_scalar(f32::NAN), None); + + assert_eq!((10.0..f32::NAN).cmp_scalar(15.0), None); + assert_eq!((f32::NAN..20.0).cmp_scalar(15.0), None); + assert_eq!((10.0..=f32::NAN).cmp_scalar(15.0), None); + assert_eq!((f32::NAN..=20.0).cmp_scalar(15.0), None); + assert_eq!((f32::NAN..).cmp_scalar(15.0), None); + assert_eq!((..f32::NAN).cmp_scalar(15.0), None); + assert_eq!((..=f32::NAN).cmp_scalar(15.0), None); }