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

Lines changed: 66 additions & 62 deletions
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

Lines changed: 23 additions & 12 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy