Implement useful steps_between for all integers
We can use `usize::try_from` to convert steps from any size of integer. This enables a meaningful `size_hint()` for larger ranges, rather than always just `(0, None)`. Now they return the true `(len, Some(len))` when it fits, otherwise `(usize::MAX, None)` for overflow.
This commit is contained in:
parent
d91b32b4dc
commit
01162d86c5
@ -68,11 +68,9 @@ macro_rules! step_impl_unsigned {
|
||||
issue = "42168")]
|
||||
impl Step for $t {
|
||||
#[inline]
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
|
||||
if *start < *end {
|
||||
// Note: We assume $t <= usize here
|
||||
Some((*end - *start) as usize)
|
||||
usize::try_from(*end - *start).ok()
|
||||
} else {
|
||||
Some(0)
|
||||
}
|
||||
@ -98,13 +96,11 @@ macro_rules! step_impl_signed {
|
||||
issue = "42168")]
|
||||
impl Step for $t {
|
||||
#[inline]
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
|
||||
if *start < *end {
|
||||
// Note: We assume $t <= isize here
|
||||
// Use .wrapping_sub and cast to usize to compute the
|
||||
// difference that may not fit inside the range of isize.
|
||||
Some((*end as isize).wrapping_sub(*start as isize) as usize)
|
||||
// Use .wrapping_sub and cast to unsigned to compute the
|
||||
// difference that may not fit inside the range of $t.
|
||||
usize::try_from(end.wrapping_sub(*start) as $unsigned).ok()
|
||||
} else {
|
||||
Some(0)
|
||||
}
|
||||
@ -134,46 +130,9 @@ fn add_usize(&self, n: usize) -> Option<Self> {
|
||||
)*)
|
||||
}
|
||||
|
||||
macro_rules! step_impl_no_between {
|
||||
($($t:ty)*) => ($(
|
||||
#[unstable(feature = "step_trait",
|
||||
reason = "likely to be replaced by finer-grained traits",
|
||||
issue = "42168")]
|
||||
impl Step for $t {
|
||||
#[inline]
|
||||
fn steps_between(_start: &Self, _end: &Self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_usize(&self, n: usize) -> Option<Self> {
|
||||
self.checked_add(n as $t)
|
||||
}
|
||||
|
||||
step_identical_methods!();
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
step_impl_unsigned!(usize u8 u16);
|
||||
#[cfg(not(target_pointer_width = "16"))]
|
||||
step_impl_unsigned!(u32);
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
step_impl_no_between!(u32);
|
||||
step_impl_unsigned!(usize u8 u16 u32 u64 u128);
|
||||
step_impl_signed!([isize: usize] [i8: u8] [i16: u16]);
|
||||
#[cfg(not(target_pointer_width = "16"))]
|
||||
step_impl_signed!([i32: u32]);
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
step_impl_no_between!(i32);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
step_impl_unsigned!(u64);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
step_impl_signed!([i64: u64]);
|
||||
// If the target pointer width is not 64-bits, we
|
||||
// assume here that it is less than 64-bits.
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
step_impl_no_between!(u64 i64);
|
||||
step_impl_no_between!(u128 i128);
|
||||
step_impl_signed!([i32: u32] [i64: u64] [i128: u128]);
|
||||
|
||||
macro_rules! range_exact_iter_impl {
|
||||
($($t:ty)*) => ($(
|
||||
@ -229,7 +188,7 @@ fn next(&mut self) -> Option<A> {
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match Step::steps_between(&self.start, &self.end) {
|
||||
Some(hint) => (hint, Some(hint)),
|
||||
None => (0, None)
|
||||
None => (usize::MAX, None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,7 +309,7 @@ fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
|
||||
match Step::steps_between(&self.start, &self.end) {
|
||||
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
|
||||
None => (0, None),
|
||||
None => (usize::MAX, None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::cell::Cell;
|
||||
use core::convert::TryFrom;
|
||||
use core::iter::*;
|
||||
use core::{i8, i16, isize};
|
||||
use core::usize;
|
||||
@ -1800,6 +1801,54 @@ fn test_range_inclusive_folds() {
|
||||
assert!(it.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_size_hint() {
|
||||
use core::usize::MAX as UMAX;
|
||||
assert_eq!((0..100usize).size_hint(), (100, Some(100)));
|
||||
assert_eq!((0..UMAX).size_hint(), (UMAX, Some(UMAX)));
|
||||
|
||||
let umax = u128::try_from(UMAX).unwrap();
|
||||
assert_eq!((0..100u128).size_hint(), (100, Some(100)));
|
||||
assert_eq!((0..umax).size_hint(), (UMAX, Some(UMAX)));
|
||||
assert_eq!((0..umax + 1).size_hint(), (UMAX, None));
|
||||
|
||||
use core::isize::{MAX as IMAX, MIN as IMIN};
|
||||
assert_eq!((-100..100isize).size_hint(), (200, Some(200)));
|
||||
assert_eq!((IMIN..IMAX).size_hint(), (UMAX, Some(UMAX)));
|
||||
|
||||
let imin = i128::try_from(IMIN).unwrap();
|
||||
let imax = i128::try_from(IMAX).unwrap();
|
||||
assert_eq!((-100..100i128).size_hint(), (200, Some(200)));
|
||||
assert_eq!((imin..imax).size_hint(), (UMAX, Some(UMAX)));
|
||||
assert_eq!((imin..imax + 1).size_hint(), (UMAX, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_inclusive_size_hint() {
|
||||
use core::usize::MAX as UMAX;
|
||||
assert_eq!((0..=100usize).size_hint(), (101, Some(101)));
|
||||
assert_eq!((0..=UMAX - 1).size_hint(), (UMAX, Some(UMAX)));
|
||||
assert_eq!((0..=UMAX).size_hint(), (UMAX, None));
|
||||
|
||||
let umax = u128::try_from(UMAX).unwrap();
|
||||
assert_eq!((0..=100u128).size_hint(), (101, Some(101)));
|
||||
assert_eq!((0..=umax - 1).size_hint(), (UMAX, Some(UMAX)));
|
||||
assert_eq!((0..=umax).size_hint(), (UMAX, None));
|
||||
assert_eq!((0..=umax + 1).size_hint(), (UMAX, None));
|
||||
|
||||
use core::isize::{MAX as IMAX, MIN as IMIN};
|
||||
assert_eq!((-100..=100isize).size_hint(), (201, Some(201)));
|
||||
assert_eq!((IMIN..=IMAX - 1).size_hint(), (UMAX, Some(UMAX)));
|
||||
assert_eq!((IMIN..=IMAX).size_hint(), (UMAX, None));
|
||||
|
||||
let imin = i128::try_from(IMIN).unwrap();
|
||||
let imax = i128::try_from(IMAX).unwrap();
|
||||
assert_eq!((-100..=100i128).size_hint(), (201, Some(201)));
|
||||
assert_eq!((imin..=imax - 1).size_hint(), (UMAX, Some(UMAX)));
|
||||
assert_eq!((imin..=imax).size_hint(), (UMAX, None));
|
||||
assert_eq!((imin..=imax + 1).size_hint(), (UMAX, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_repeat() {
|
||||
let mut it = repeat(42);
|
||||
|
Loading…
Reference in New Issue
Block a user