Skip to content

Commit 23a1b31

Browse files
committed
Auto merge of rust-lang#130867 - michirakara:steps_between, r=dtolnay
distinguish overflow and unimplemented in Step::steps_between
2 parents 40d6e2c + 276c0fc commit 23a1b31

File tree

2 files changed

+89
-74
lines changed

2 files changed

+89
-74
lines changed

core/src/iter/range.rs

+66-62
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,21 @@ unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u6
2222
/// The *predecessor* operation moves towards values that compare lesser.
2323
#[unstable(feature = "step_trait", issue = "42168")]
2424
pub trait Step: Clone + PartialOrd + Sized {
25-
/// Returns the number of *successor* steps required to get from `start` to `end`.
25+
/// Returns the bounds on the number of *successor* steps required to get from `start` to `end`
26+
/// like [`Iterator::size_hint()`][Iterator::size_hint()].
2627
///
27-
/// Returns `None` if the number of steps would overflow `usize`
28-
/// (or is infinite, or if `end` would never be reached).
28+
/// Returns `(usize::MAX, None)` if the number of steps would overflow `usize`, or is infinite.
2929
///
3030
/// # Invariants
3131
///
3232
/// For any `a`, `b`, and `n`:
3333
///
34-
/// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward_checked(&a, n) == Some(b)`
35-
/// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&b, n) == Some(a)`
36-
/// * `steps_between(&a, &b) == Some(n)` only if `a <= b`
37-
/// * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b`
38-
/// * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`;
39-
/// this is the case when it would require more than `usize::MAX` steps to get to `b`
40-
/// * `steps_between(&a, &b) == None` if `a > b`
41-
fn steps_between(start: &Self, end: &Self) -> Option<usize>;
34+
/// * `steps_between(&a, &b) == (n, Some(n))` if and only if `Step::forward_checked(&a, n) == Some(b)`
35+
/// * `steps_between(&a, &b) == (n, Some(n))` if and only if `Step::backward_checked(&b, n) == Some(a)`
36+
/// * `steps_between(&a, &b) == (n, Some(n))` only if `a <= b`
37+
/// * Corollary: `steps_between(&a, &b) == (0, Some(0))` if and only if `a == b`
38+
/// * `steps_between(&a, &b) == (0, None)` if `a > b`
39+
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>);
4240

4341
/// Returns the value that would be obtained by taking the *successor*
4442
/// of `self` `count` times.
@@ -169,7 +167,7 @@ pub trait Step: Clone + PartialOrd + Sized {
169167
/// For any `a`:
170168
///
171169
/// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)`
172-
/// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`,
170+
/// * if there exists `b`, `n` such that `steps_between(&b, &a) == (n, Some(n))`,
173171
/// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`.
174172
/// * Corollary: `Step::backward_unchecked(a, 0)` is always safe.
175173
///
@@ -261,12 +259,13 @@ macro_rules! step_integer_impls {
261259
step_unsigned_methods!();
262260

263261
#[inline]
264-
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
262+
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
265263
if *start <= *end {
266264
// This relies on $u_narrower <= usize
267-
Some((*end - *start) as usize)
265+
let steps = (*end - *start) as usize;
266+
(steps, Some(steps))
268267
} else {
269-
None
268+
(0, None)
270269
}
271270
}
272271

@@ -294,16 +293,17 @@ macro_rules! step_integer_impls {
294293
step_signed_methods!($u_narrower);
295294

296295
#[inline]
297-
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
296+
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
298297
if *start <= *end {
299298
// This relies on $i_narrower <= usize
300299
//
301300
// Casting to isize extends the width but preserves the sign.
302301
// Use wrapping_sub in isize space and cast to usize to compute
303302
// the difference that might not fit inside the range of isize.
304-
Some((*end as isize).wrapping_sub(*start as isize) as usize)
303+
let steps = (*end as isize).wrapping_sub(*start as isize) as usize;
304+
(steps, Some(steps))
305305
} else {
306-
None
306+
(0, None)
307307
}
308308
}
309309

@@ -359,11 +359,15 @@ macro_rules! step_integer_impls {
359359
step_unsigned_methods!();
360360

361361
#[inline]
362-
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
362+
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
363363
if *start <= *end {
364-
usize::try_from(*end - *start).ok()
364+
if let Ok(steps) = usize::try_from(*end - *start) {
365+
(steps, Some(steps))
366+
} else {
367+
(usize::MAX, None)
368+
}
365369
} else {
366-
None
370+
(0, None)
367371
}
368372
}
369373

@@ -385,16 +389,22 @@ macro_rules! step_integer_impls {
385389
step_signed_methods!($u_wider);
386390

387391
#[inline]
388-
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
392+
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
389393
if *start <= *end {
390394
match end.checked_sub(*start) {
391-
Some(result) => usize::try_from(result).ok(),
395+
Some(result) => {
396+
if let Ok(steps) = usize::try_from(result) {
397+
(steps, Some(steps))
398+
} else {
399+
(usize::MAX, None)
400+
}
401+
}
392402
// If the difference is too big for e.g. i128,
393403
// it's also gonna be too big for usize with fewer bits.
394-
None => None,
404+
None => (usize::MAX, None),
395405
}
396406
} else {
397-
None
407+
(0, None)
398408
}
399409
}
400410

@@ -433,18 +443,26 @@ step_integer_impls! {
433443
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
434444
impl Step for char {
435445
#[inline]
436-
fn steps_between(&start: &char, &end: &char) -> Option<usize> {
446+
fn steps_between(&start: &char, &end: &char) -> (usize, Option<usize>) {
437447
let start = start as u32;
438448
let end = end as u32;
439449
if start <= end {
440450
let count = end - start;
441451
if start < 0xD800 && 0xE000 <= end {
442-
usize::try_from(count - 0x800).ok()
452+
if let Ok(steps) = usize::try_from(count - 0x800) {
453+
(steps, Some(steps))
454+
} else {
455+
(usize::MAX, None)
456+
}
443457
} else {
444-
usize::try_from(count).ok()
458+
if let Ok(steps) = usize::try_from(count) {
459+
(steps, Some(steps))
460+
} else {
461+
(usize::MAX, None)
462+
}
445463
}
446464
} else {
447-
None
465+
(0, None)
448466
}
449467
}
450468

@@ -512,7 +530,7 @@ impl Step for char {
512530
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
513531
impl Step for AsciiChar {
514532
#[inline]
515-
fn steps_between(&start: &AsciiChar, &end: &AsciiChar) -> Option<usize> {
533+
fn steps_between(&start: &AsciiChar, &end: &AsciiChar) -> (usize, Option<usize>) {
516534
Step::steps_between(&start.to_u8(), &end.to_u8())
517535
}
518536

@@ -554,7 +572,7 @@ impl Step for AsciiChar {
554572
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
555573
impl Step for Ipv4Addr {
556574
#[inline]
557-
fn steps_between(&start: &Ipv4Addr, &end: &Ipv4Addr) -> Option<usize> {
575+
fn steps_between(&start: &Ipv4Addr, &end: &Ipv4Addr) -> (usize, Option<usize>) {
558576
u32::steps_between(&start.to_bits(), &end.to_bits())
559577
}
560578

@@ -586,7 +604,7 @@ impl Step for Ipv4Addr {
586604
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
587605
impl Step for Ipv6Addr {
588606
#[inline]
589-
fn steps_between(&start: &Ipv6Addr, &end: &Ipv6Addr) -> Option<usize> {
607+
fn steps_between(&start: &Ipv6Addr, &end: &Ipv6Addr) -> (usize, Option<usize>) {
590608
u128::steps_between(&start.to_bits(), &end.to_bits())
591609
}
592610

@@ -690,11 +708,8 @@ impl<A: Step> RangeIteratorImpl for ops::Range<A> {
690708

691709
#[inline]
692710
default fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
693-
let available = if self.start <= self.end {
694-
Step::steps_between(&self.start, &self.end).unwrap_or(usize::MAX)
695-
} else {
696-
0
697-
};
711+
let steps = Step::steps_between(&self.start, &self.end);
712+
let available = steps.1.unwrap_or(steps.0);
698713

699714
let taken = available.min(n);
700715

@@ -731,11 +746,8 @@ impl<A: Step> RangeIteratorImpl for ops::Range<A> {
731746

732747
#[inline]
733748
default fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
734-
let available = if self.start <= self.end {
735-
Step::steps_between(&self.start, &self.end).unwrap_or(usize::MAX)
736-
} else {
737-
0
738-
};
749+
let steps = Step::steps_between(&self.start, &self.end);
750+
let available = steps.1.unwrap_or(steps.0);
739751

740752
let taken = available.min(n);
741753

@@ -775,11 +787,8 @@ impl<T: TrustedStep> RangeIteratorImpl for ops::Range<T> {
775787

776788
#[inline]
777789
fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
778-
let available = if self.start <= self.end {
779-
Step::steps_between(&self.start, &self.end).unwrap_or(usize::MAX)
780-
} else {
781-
0
782-
};
790+
let steps = Step::steps_between(&self.start, &self.end);
791+
let available = steps.1.unwrap_or(steps.0);
783792

784793
let taken = available.min(n);
785794

@@ -819,11 +828,8 @@ impl<T: TrustedStep> RangeIteratorImpl for ops::Range<T> {
819828

820829
#[inline]
821830
fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
822-
let available = if self.start <= self.end {
823-
Step::steps_between(&self.start, &self.end).unwrap_or(usize::MAX)
824-
} else {
825-
0
826-
};
831+
let steps = Step::steps_between(&self.start, &self.end);
832+
let available = steps.1.unwrap_or(steps.0);
827833

828834
let taken = available.min(n);
829835

@@ -846,8 +852,7 @@ impl<A: Step> Iterator for ops::Range<A> {
846852
#[inline]
847853
fn size_hint(&self) -> (usize, Option<usize>) {
848854
if self.start < self.end {
849-
let hint = Step::steps_between(&self.start, &self.end);
850-
(hint.unwrap_or(usize::MAX), hint)
855+
Step::steps_between(&self.start, &self.end)
851856
} else {
852857
(0, Some(0))
853858
}
@@ -856,7 +861,7 @@ impl<A: Step> Iterator for ops::Range<A> {
856861
#[inline]
857862
fn count(self) -> usize {
858863
if self.start < self.end {
859-
Step::steps_between(&self.start, &self.end).expect("count overflowed usize")
864+
Step::steps_between(&self.start, &self.end).1.expect("count overflowed usize")
860865
} else {
861866
0
862867
}
@@ -980,11 +985,11 @@ impl<A: Step> DoubleEndedIterator for ops::Range<A> {
980985
// Safety:
981986
// The following invariants for `Step::steps_between` exist:
982987
//
983-
// > * `steps_between(&a, &b) == Some(n)` only if `a <= b`
984-
// > * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`;
988+
// > * `steps_between(&a, &b) == (n, Some(n))` only if `a <= b`
989+
// > * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != (n, None)`;
985990
// > this is the case when it would require more than `usize::MAX` steps to
986991
// > get to `b`
987-
// > * `steps_between(&a, &b) == None` if `a > b`
992+
// > * `steps_between(&a, &b) == (0, None)` if `a > b`
988993
//
989994
// The first invariant is what is generally required for `TrustedLen` to be
990995
// sound. The note addendum satisfies an additional `TrustedLen` invariant.
@@ -1253,10 +1258,8 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
12531258
return (0, Some(0));
12541259
}
12551260

1256-
match Step::steps_between(&self.start, &self.end) {
1257-
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
1258-
None => (usize::MAX, None),
1259-
}
1261+
let hint = Step::steps_between(&self.start, &self.end);
1262+
(hint.0.saturating_add(1), hint.1.and_then(|steps| steps.checked_add(1)))
12601263
}
12611264

12621265
#[inline]
@@ -1266,6 +1269,7 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
12661269
}
12671270

12681271
Step::steps_between(&self.start, &self.end)
1272+
.1
12691273
.and_then(|steps| steps.checked_add(1))
12701274
.expect("count overflowed usize")
12711275
}

core/tests/iter/traits/step.rs

+23-12
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,37 @@ use core::iter::*;
22

33
#[test]
44
fn test_steps_between() {
5-
assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize));
6-
assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize));
7-
assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize));
8-
assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize));
9-
assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize));
10-
assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize));
5+
assert_eq!(Step::steps_between(&20_u8, &200_u8), (180_usize, Some(180_usize)));
6+
assert_eq!(Step::steps_between(&-20_i8, &80_i8), (100_usize, Some(100_usize)));
7+
assert_eq!(Step::steps_between(&-120_i8, &80_i8), (200_usize, Some(200_usize)));
8+
assert_eq!(
9+
Step::steps_between(&20_u32, &4_000_100_u32),
10+
(4_000_080_usize, Some(4_000_080_usize))
11+
);
12+
assert_eq!(Step::steps_between(&-20_i32, &80_i32), (100_usize, Some(100_usize)));
13+
assert_eq!(
14+
Step::steps_between(&-2_000_030_i32, &2_000_050_i32),
15+
(4_000_080_usize, Some(4_000_080_usize))
16+
);
1117

1218
// Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms
1319

14-
assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize));
15-
assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize));
20+
assert_eq!(Step::steps_between(&20_u128, &200_u128), (180_usize, Some(180_usize)));
21+
assert_eq!(Step::steps_between(&-20_i128, &80_i128), (100_usize, Some(100_usize)));
1622
if cfg!(target_pointer_width = "64") {
17-
assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX));
23+
assert_eq!(
24+
Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128),
25+
(usize::MAX, Some(usize::MAX))
26+
);
1827
}
19-
assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None);
20-
assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None);
28+
assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), (usize::MAX, None));
29+
assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), (usize::MAX, None));
2130
assert_eq!(
2231
Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,),
23-
None,
32+
(usize::MAX, None),
2433
);
34+
35+
assert_eq!(Step::steps_between(&100_u32, &10_u32), (0, None));
2536
}
2637

2738
#[test]

0 commit comments

Comments
 (0)