Skip to content

Commit 5e8998a

Browse files
authored
Rollup merge of rust-lang#59444 - cuviper:steps_between, r=scottmcm
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.
2 parents d86a8f3 + a548d83 commit 5e8998a

File tree

2 files changed

+71
-51
lines changed

2 files changed

+71
-51
lines changed

src/libcore/iter/range.rs

+10-51
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,9 @@ macro_rules! step_impl_unsigned {
6868
issue = "42168")]
6969
impl Step for $t {
7070
#[inline]
71-
#[allow(trivial_numeric_casts)]
7271
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
7372
if *start < *end {
74-
// Note: We assume $t <= usize here
75-
Some((*end - *start) as usize)
73+
usize::try_from(*end - *start).ok()
7674
} else {
7775
Some(0)
7876
}
@@ -98,13 +96,11 @@ macro_rules! step_impl_signed {
9896
issue = "42168")]
9997
impl Step for $t {
10098
#[inline]
101-
#[allow(trivial_numeric_casts)]
10299
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
103100
if *start < *end {
104-
// Note: We assume $t <= isize here
105-
// Use .wrapping_sub and cast to usize to compute the
106-
// difference that may not fit inside the range of isize.
107-
Some((*end as isize).wrapping_sub(*start as isize) as usize)
101+
// Use .wrapping_sub and cast to unsigned to compute the
102+
// difference that may not fit inside the range of $t.
103+
usize::try_from(end.wrapping_sub(*start) as $unsigned).ok()
108104
} else {
109105
Some(0)
110106
}
@@ -134,46 +130,9 @@ macro_rules! step_impl_signed {
134130
)*)
135131
}
136132

137-
macro_rules! step_impl_no_between {
138-
($($t:ty)*) => ($(
139-
#[unstable(feature = "step_trait",
140-
reason = "likely to be replaced by finer-grained traits",
141-
issue = "42168")]
142-
impl Step for $t {
143-
#[inline]
144-
fn steps_between(_start: &Self, _end: &Self) -> Option<usize> {
145-
None
146-
}
147-
148-
#[inline]
149-
fn add_usize(&self, n: usize) -> Option<Self> {
150-
self.checked_add(n as $t)
151-
}
152-
153-
step_identical_methods!();
154-
}
155-
)*)
156-
}
157-
158-
step_impl_unsigned!(usize u8 u16);
159-
#[cfg(not(target_pointer_width = "16"))]
160-
step_impl_unsigned!(u32);
161-
#[cfg(target_pointer_width = "16")]
162-
step_impl_no_between!(u32);
133+
step_impl_unsigned!(usize u8 u16 u32 u64 u128);
163134
step_impl_signed!([isize: usize] [i8: u8] [i16: u16]);
164-
#[cfg(not(target_pointer_width = "16"))]
165-
step_impl_signed!([i32: u32]);
166-
#[cfg(target_pointer_width = "16")]
167-
step_impl_no_between!(i32);
168-
#[cfg(target_pointer_width = "64")]
169-
step_impl_unsigned!(u64);
170-
#[cfg(target_pointer_width = "64")]
171-
step_impl_signed!([i64: u64]);
172-
// If the target pointer width is not 64-bits, we
173-
// assume here that it is less than 64-bits.
174-
#[cfg(not(target_pointer_width = "64"))]
175-
step_impl_no_between!(u64 i64);
176-
step_impl_no_between!(u128 i128);
135+
step_impl_signed!([i32: u32] [i64: u64] [i128: u128]);
177136

178137
macro_rules! range_exact_iter_impl {
179138
($($t:ty)*) => ($(
@@ -229,7 +188,7 @@ impl<A: Step> Iterator for ops::Range<A> {
229188
fn size_hint(&self) -> (usize, Option<usize>) {
230189
match Step::steps_between(&self.start, &self.end) {
231190
Some(hint) => (hint, Some(hint)),
232-
None => (0, None)
191+
None => (usize::MAX, None)
233192
}
234193
}
235194

@@ -273,8 +232,8 @@ range_incl_exact_iter_impl!(u8 u16 i8 i16);
273232
//
274233
// They need to guarantee that .size_hint() is either exact, or that
275234
// the upper bound is None when it does not fit the type limits.
276-
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
277-
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
235+
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
236+
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
278237

279238
#[stable(feature = "rust1", since = "1.0.0")]
280239
impl<A: Step> DoubleEndedIterator for ops::Range<A> {
@@ -350,7 +309,7 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
350309

351310
match Step::steps_between(&self.start, &self.end) {
352311
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
353-
None => (0, None),
312+
None => (usize::MAX, None),
354313
}
355314
}
356315

src/libcore/tests/iter.rs

+61
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::cell::Cell;
2+
use core::convert::TryFrom;
23
use core::iter::*;
34
use core::{i8, i16, isize};
45
use core::usize;
@@ -1800,6 +1801,66 @@ fn test_range_inclusive_folds() {
18001801
assert!(it.is_empty());
18011802
}
18021803

1804+
#[test]
1805+
fn test_range_size_hint() {
1806+
use core::usize::MAX as UMAX;
1807+
assert_eq!((0..0usize).size_hint(), (0, Some(0)));
1808+
assert_eq!((0..100usize).size_hint(), (100, Some(100)));
1809+
assert_eq!((0..UMAX).size_hint(), (UMAX, Some(UMAX)));
1810+
1811+
let umax = u128::try_from(UMAX).unwrap();
1812+
assert_eq!((0..0u128).size_hint(), (0, Some(0)));
1813+
assert_eq!((0..100u128).size_hint(), (100, Some(100)));
1814+
assert_eq!((0..umax).size_hint(), (UMAX, Some(UMAX)));
1815+
assert_eq!((0..umax + 1).size_hint(), (UMAX, None));
1816+
1817+
use core::isize::{MAX as IMAX, MIN as IMIN};
1818+
assert_eq!((0..0isize).size_hint(), (0, Some(0)));
1819+
assert_eq!((-100..100isize).size_hint(), (200, Some(200)));
1820+
assert_eq!((IMIN..IMAX).size_hint(), (UMAX, Some(UMAX)));
1821+
1822+
let imin = i128::try_from(IMIN).unwrap();
1823+
let imax = i128::try_from(IMAX).unwrap();
1824+
assert_eq!((0..0i128).size_hint(), (0, Some(0)));
1825+
assert_eq!((-100..100i128).size_hint(), (200, Some(200)));
1826+
assert_eq!((imin..imax).size_hint(), (UMAX, Some(UMAX)));
1827+
assert_eq!((imin..imax + 1).size_hint(), (UMAX, None));
1828+
}
1829+
1830+
#[test]
1831+
fn test_range_inclusive_size_hint() {
1832+
use core::usize::MAX as UMAX;
1833+
assert_eq!((1..=0usize).size_hint(), (0, Some(0)));
1834+
assert_eq!((0..=0usize).size_hint(), (1, Some(1)));
1835+
assert_eq!((0..=100usize).size_hint(), (101, Some(101)));
1836+
assert_eq!((0..=UMAX - 1).size_hint(), (UMAX, Some(UMAX)));
1837+
assert_eq!((0..=UMAX).size_hint(), (UMAX, None));
1838+
1839+
let umax = u128::try_from(UMAX).unwrap();
1840+
assert_eq!((1..=0u128).size_hint(), (0, Some(0)));
1841+
assert_eq!((0..=0u128).size_hint(), (1, Some(1)));
1842+
assert_eq!((0..=100u128).size_hint(), (101, Some(101)));
1843+
assert_eq!((0..=umax - 1).size_hint(), (UMAX, Some(UMAX)));
1844+
assert_eq!((0..=umax).size_hint(), (UMAX, None));
1845+
assert_eq!((0..=umax + 1).size_hint(), (UMAX, None));
1846+
1847+
use core::isize::{MAX as IMAX, MIN as IMIN};
1848+
assert_eq!((0..=-1isize).size_hint(), (0, Some(0)));
1849+
assert_eq!((0..=0isize).size_hint(), (1, Some(1)));
1850+
assert_eq!((-100..=100isize).size_hint(), (201, Some(201)));
1851+
assert_eq!((IMIN..=IMAX - 1).size_hint(), (UMAX, Some(UMAX)));
1852+
assert_eq!((IMIN..=IMAX).size_hint(), (UMAX, None));
1853+
1854+
let imin = i128::try_from(IMIN).unwrap();
1855+
let imax = i128::try_from(IMAX).unwrap();
1856+
assert_eq!((0..=-1i128).size_hint(), (0, Some(0)));
1857+
assert_eq!((0..=0i128).size_hint(), (1, Some(1)));
1858+
assert_eq!((-100..=100i128).size_hint(), (201, Some(201)));
1859+
assert_eq!((imin..=imax - 1).size_hint(), (UMAX, Some(UMAX)));
1860+
assert_eq!((imin..=imax).size_hint(), (UMAX, None));
1861+
assert_eq!((imin..=imax + 1).size_hint(), (UMAX, None));
1862+
}
1863+
18031864
#[test]
18041865
fn test_repeat() {
18051866
let mut it = repeat(42);

0 commit comments

Comments
 (0)