Skip to content

Redesign the std::iter::Step trait, tweak related iterator impls for ranges #43127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
467 changes: 290 additions & 177 deletions src/libcore/iter/range.rs
Original file line number Diff line number Diff line change
@@ -10,170 +10,235 @@

use convert::TryFrom;
use mem;
use ops::{self, Add, Sub};
use ops;
use usize;

use super::{FusedIterator, TrustedLen};

/// Objects that can be stepped over in both directions.
/// Objects that have a notion of *successor* and *predecessor*
/// for the purpose of range iterators.
///
/// The `steps_between` function provides a way to efficiently compare
/// two `Step` objects.
/// This trait is `unsafe` because implementations of the `unsafe` trait `TrustedLen`
/// depend on its implementations being correct.
#[unstable(feature = "step_trait",
reason = "likely to be replaced by finer-grained traits",
reason = "recently redesigned",
issue = "42168")]
pub trait Step: Clone + PartialOrd + Sized {
/// Returns the number of steps between two step objects. The count is
/// inclusive of `start` and exclusive of `end`.
pub unsafe trait Step: Clone + PartialOrd + Sized {
/// Returns the number of *successor* steps needed to get from `start` to `end`.
///
/// Returns `None` if it is not possible to calculate `steps_between`
/// without overflow.
/// Returns `None` if that number would overflow `usize`
/// (or is infinite, if `end` would never be reached).
///
/// This must hold for any `a`, `b`, and `n`:
///
/// * `steps_between(&a, &b) == Some(0)` if and only if `a >= b`.
/// * `steps_between(&a, &b) == Some(n)` if and only if `a.forward(n) == Some(b)`
/// * `steps_between(&a, &b) == Some(n)` if and only if `b.backward(n) == Some(a)`
fn steps_between(start: &Self, end: &Self) -> Option<usize>;

/// Replaces this step with `1`, returning itself
fn replace_one(&mut self) -> Self;

/// Replaces this step with `0`, returning itself
fn replace_zero(&mut self) -> Self;

/// Adds one to this step, returning the result
fn add_one(&self) -> Self;

/// Subtracts one to this step, returning the result
fn sub_one(&self) -> Self;
/// Returns the value that would be obtained by taking the *successor* of `self`,
/// `step_count` times.
///
/// Returns `None` if this would overflow the range of values supported by the type `Self`.
///
/// Note: `step_count == 1` is a common case,
/// used for example in `Iterator::next` for ranges.
Copy link
Member

@scottmcm scottmcm Jul 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth a fn forward_one(&self) -> Option<Self> { self.forward(1) } (or call it successor, perhaps, like the comment here does) so the common case is overridable if needed? I suppose it's always addable later if needed.

///
/// This must hold for any `a`, `n`, and `m` where `n + m` doesn’t overflow:
///
/// * `a.forward(n).and_then(|x| x.forward(m)) == a.forward(n + m)`
fn forward(&self, step_count: usize) -> Option<Self>;

/// Add an usize, returning None on overflow
fn add_usize(&self, n: usize) -> Option<Self>;
/// Returns the value that would be obtained by taking the *predecessor* of `self`,
/// `step_count` times.
///
/// Returns `None` if this would overflow the range of values supported by the type `Self`.
///
/// Note: `step_count == 1` is a common case,
/// used for example in `Iterator::next_back` for ranges.
///
/// This must hold for any `a`, `n`, and `m` where `n + m` doesn’t overflow:
///
/// * `a.backward(n).and_then(|x| x.backward(m)) == a.backward(n + m)`
fn backward(&self, step_count: usize) -> Option<Self>;
}

// These are still macro-generated because the integer literals resolve to different types.
macro_rules! step_identical_methods {
() => {
#[inline]
fn replace_one(&mut self) -> Self {
mem::replace(self, 1)
}
macro_rules! step_integer_impls {
(
narrower than or same width as usize:
$( [ $narrower_unsigned:ident $narrower_signed: ident ] ),+;
wider than usize:
$( [ $wider_unsigned:ident $wider_signed: ident ] ),+;
) => {
$(
#[unstable(feature = "step_trait",
reason = "recently redesigned",
issue = "42168")]
unsafe impl Step for $narrower_unsigned {
#[inline]
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
if *start < *end {
// This relies on $narrower_unsigned <= usize
Some((*end - *start) as usize)
} else {
Some(0)
}
}

#[inline]
fn replace_zero(&mut self) -> Self {
mem::replace(self, 0)
}
#[inline]
fn forward(&self, n: usize) -> Option<Self> {
match Self::try_from(n) {
Ok(n_converted) => self.checked_add(n_converted),
Err(_) => None, // if n is out of range, `something_unsigned + n` is too
}
}

#[inline]
fn add_one(&self) -> Self {
Add::add(*self, 1)
}
#[inline]
fn backward(&self, n: usize) -> Option<Self> {
match Self::try_from(n) {
Ok(n_converted) => self.checked_sub(n_converted),
Err(_) => None, // if n is out of range, `something_in_range - n` is too
}
}
}

#[inline]
fn sub_one(&self) -> Self {
Sub::sub(*self, 1)
}
}
}
#[unstable(feature = "step_trait",
reason = "recently redesigned",
issue = "42168")]
unsafe impl Step for $narrower_signed {
#[inline]
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
if *start < *end {
// This relies on $narrower_signed <= usize
//
// Casting to isize extends the width but preserves the sign.
// Use wrapping_sub in isize space 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)
} else {
Some(0)
}
}

macro_rules! step_impl_unsigned {
($($t:ty)*) => ($(
#[unstable(feature = "step_trait",
reason = "likely to be replaced by finer-grained traits",
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)
} else {
Some(0)
#[inline]
fn forward(&self, n: usize) -> Option<Self> {
match <$narrower_unsigned>::try_from(n) {
Ok(n_unsigned) => {
// Wrapping in unsigned space handles cases like
// `-120_i8.forward(200) == Some(80_i8)`,
// even though 200_usize is out of range for i8.
let self_unsigned = *self as $narrower_unsigned;
let wrapped = self_unsigned.wrapping_add(n_unsigned) as Self;
if wrapped >= *self {
Some(wrapped)
} else {
None // Addition overflowed
}
}
// If n is out of range of e.g. u8,
// then it is bigger than the entire range for i8 is wide
// so `any_i8 + n` would overflow i8.
Err(_) => None,
}
}
#[inline]
fn backward(&self, n: usize) -> Option<Self> {
match <$narrower_unsigned>::try_from(n) {
Ok(n_unsigned) => {
// Wrapping in unsigned space handles cases like
// `-120_i8.forward(200) == Some(80_i8)`,
// even though 200_usize is out of range for i8.
let self_unsigned = *self as $narrower_unsigned;
let wrapped = self_unsigned.wrapping_sub(n_unsigned) as Self;
if wrapped <= *self {
Some(wrapped)
} else {
None // Subtraction underflowed
}
}
// If n is out of range of e.g. u8,
// then it is bigger than the entire range for i8 is wide
// so `any_i8 - n` would underflow i8.
Err(_) => None,
}
}
}
)+

$(
#[unstable(feature = "step_trait",
reason = "recently redesigned",
issue = "42168")]
unsafe impl Step for $wider_unsigned {
#[inline]
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
if *start < *end {
usize::try_from(*end - *start).ok()
} else {
Some(0)
}
}

#[inline]
fn add_usize(&self, n: usize) -> Option<Self> {
match <$t>::try_from(n) {
Ok(n_as_t) => self.checked_add(n_as_t),
Err(_) => None,
#[inline]
fn forward(&self, n: usize) -> Option<Self> {
self.checked_add(n as Self)
}
}

step_identical_methods!();
}
)*)
}
macro_rules! step_impl_signed {
($( [$t:ty : $unsigned:ty] )*) => ($(
#[unstable(feature = "step_trait",
reason = "likely to be replaced by finer-grained traits",
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)
} else {
Some(0)
#[inline]
fn backward(&self, n: usize) -> Option<Self> {
self.checked_sub(n as Self)
}
}

#[inline]
fn add_usize(&self, n: usize) -> Option<Self> {
match <$unsigned>::try_from(n) {
Ok(n_as_unsigned) => {
// Wrapping in unsigned space handles cases like
// `-120_i8.add_usize(200) == Some(80_i8)`,
// even though 200_usize is out of range for i8.
let wrapped = (*self as $unsigned).wrapping_add(n_as_unsigned) as $t;
if wrapped >= *self {
Some(wrapped)
} else {
None // Addition overflowed
#[unstable(feature = "step_trait",
reason = "recently redesigned",
issue = "42168")]
unsafe impl Step for $wider_signed {
#[inline]
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
if *start < *end {
match end.checked_sub(*start) {
Some(diff) => usize::try_from(diff).ok(),
// If the difference is too big for e.g. i128,
// it’s also gonna be too big for usize with fewer bits.
None => None
}
} else {
Some(0)
}
Err(_) => None,
}
}

step_identical_methods!();
}
)*)
}
#[inline]
fn forward(&self, n: usize) -> Option<Self> {
self.checked_add(n as 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 backward(&self, n: usize) -> Option<Self> {
self.checked_sub(n as Self)
}
}
)+
}
}

#[inline]
fn add_usize(&self, n: usize) -> Option<Self> {
self.checked_add(n as $t)
}
#[cfg(target_pointer_width = "64")]
step_integer_impls! {
narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize];
wider than usize: [u128 i128];
}

step_identical_methods!();
}
)*)
#[cfg(target_pointer_width = "32")]
step_integer_impls! {
narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize];
wider than usize: [u64 i64], [u128 i128];
}

step_impl_unsigned!(usize u8 u16 u32);
step_impl_signed!([isize: usize] [i8: u8] [i16: u16] [i32: u32]);
#[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);
#[cfg(target_pointer_width = "16")]
step_integer_impls! {
narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize];
wider than usize: [u32 i32], [u64 i64], [u128 i128];
}

macro_rules! range_exact_iter_impl {
($($t:ty)*) => ($(
@@ -191,30 +256,15 @@ macro_rules! range_incl_exact_iter_impl {
)*)
}

macro_rules! range_trusted_len_impl {
($($t:ty)*) => ($(
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl TrustedLen for ops::Range<$t> { }
)*)
}

macro_rules! range_incl_trusted_len_impl {
($($t:ty)*) => ($(
#[unstable(feature = "inclusive_range",
reason = "recently added, follows RFC",
issue = "28237")]
unsafe impl TrustedLen for ops::RangeInclusive<$t> { }
)*)
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step> Iterator for ops::Range<A> {
type Item = A;

#[inline]
fn next(&mut self) -> Option<A> {
if self.start < self.end {
let mut n = self.start.add_one();
// `start + 1` should not overflow since `end` exists such that `start < end`
let mut n = self.start.forward(1).expect("overflow in Range::next");
mem::swap(&mut n, &mut self.start);
Some(n)
} else {
@@ -224,17 +274,19 @@ impl<A: Step> Iterator for ops::Range<A> {

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// NOTE: the safety of `unsafe impl TrustedLen` depends on this being correct!
match Step::steps_between(&self.start, &self.end) {
Some(hint) => (hint, Some(hint)),
None => (0, None)
None => (usize::MAX, None)
}
}

#[inline]
fn nth(&mut self, n: usize) -> Option<A> {
if let Some(plus_n) = self.start.add_usize(n) {
if let Some(plus_n) = self.start.forward(n) {
if plus_n < self.end {
self.start = plus_n.add_one();
// `plus_n + 1` should not overflow since `end` exists such that `plus_n < end`
self.start = plus_n.forward(1).expect("overflow in Range::nth");
return Some(plus_n)
}
}
@@ -245,32 +297,53 @@ impl<A: Step> Iterator for ops::Range<A> {
}

// These macros generate `ExactSizeIterator` impls for various range types.
// Range<{u,i}64> and RangeInclusive<{u,i}{32,64,size}> are excluded
// because they cannot guarantee having a length <= usize::MAX, which is
// required by ExactSizeIterator.
range_exact_iter_impl!(usize u8 u16 u32 isize i8 i16 i32);
range_incl_exact_iter_impl!(u8 u16 i8 i16);

// These macros generate `TrustedLen` impls.
//
// They need to guarantee that .size_hint() is either exact, or that
// the upper bound is None when it does not fit the type limits.
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
// * `ExactSizeIterator::len` is required to always return an exact `usize`,
// so no range can be longer than usize::MAX.
// * For integer types in `Range<_>` this is the case for types narrower than or as wide as `usize`.
// For integer types in `RangeInclusive<_>`
// this is the case for types *strictly narrower* than `usize`
// since e.g. `(0...u64::MAX).len()` would be `u64::MAX + 1`.
// * It hurts portability to have APIs (including `impl`s)
// that are only available on some platforms,
// so only `impl`s that are always valid should exist, regardless of the current target platform.
// (NOTE: https://github.com/rust-lang/rust/issues/41619 might change this.)
// * Support exists in-tree for MSP430: https://forge.rust-lang.org/platform-support.html#tier-3
// and out-of-tree for AVR: https://github.com/avr-rust/rust
// Both are platforms where `usize` is 16 bits wide.
range_exact_iter_impl! {
usize u8 u16
isize i8 i16

// These are incorrect per the reasoning above,
// but removing them would be a breaking change as they were stabilized in Rust 1.0.0.
// So `(0..66_000_u32).len()` for example will compile without error or warnings
// on 16-bit platforms, but panic at run-time.
u32
i32
}
range_incl_exact_iter_impl! {
u8
i8
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step> DoubleEndedIterator for ops::Range<A> {
#[inline]
fn next_back(&mut self) -> Option<A> {
if self.start < self.end {
self.end = self.end.sub_one();
// `end - 1` should not overflow since `start` exists such that `start < end`
self.end = self.end.backward(1).expect("overflow in Range::nth_back");
Some(self.end.clone())
} else {
None
}
}
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<T: Step> TrustedLen for ops::Range<T> {}

#[unstable(feature = "fused", issue = "35602")]
impl<A: Step> FusedIterator for ops::Range<A> {}

@@ -280,7 +353,8 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {

#[inline]
fn next(&mut self) -> Option<A> {
let mut n = self.start.add_one();
// Overflow can happen here. Panic when it does.
let mut n = self.start.forward(1).expect("overflow in RangeFrom::next");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC this used to only panic in debug, right?

I like the change, but it might need to be called out in documentation somewhere.

mem::swap(&mut n, &mut self.start);
Some(n)
}
@@ -292,8 +366,9 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {

#[inline]
fn nth(&mut self, n: usize) -> Option<A> {
let plus_n = self.start.add_usize(n).expect("overflow in RangeFrom::nth");
self.start = plus_n.add_one();
// Overflow can happen here. Panic when it does.
let plus_n = self.start.forward(n).expect("overflow in RangeFrom::nth");
self.start = plus_n.forward(1).expect("overflow in RangeFrom::nth");
Some(plus_n)
}
}
@@ -311,12 +386,20 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {

match self.start.partial_cmp(&self.end) {
Some(Less) => {
let n = self.start.add_one();
// `start + 1` should not overflow since `end` exists such that `start < end`
let n = self.start.forward(1).expect("overflow in RangeInclusive::next");
Some(mem::replace(&mut self.start, n))
},
Some(Equal) => {
let last = self.start.replace_one();
self.end.replace_zero();
let last;
if let Some(end_plus_one) = self.end.forward(1) {
last = mem::replace(&mut self.start, end_plus_one);
} else {
last = self.start.clone();
// `start == end`, and `end + 1` underflowed.
// `start - 1` overflowing would imply a type with only one valid value?
self.end = self.start.backward(1).expect("overflow in RangeInclusive::next");
}
Some(last)
},
_ => None,
@@ -325,37 +408,53 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// This seems redundant with a similar comparison that `Step::steps_between`
// implementations need to do, but it separates the `start > end` case from `start == end`.
// `steps_between` returns `Some(0)` in both of these cases, but we only want to add 1
// in the latter.
Copy link
Member

@scottmcm scottmcm Jul 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should steps_between perhaps instead return something like Result<Option<usize>, Unreachable>? Might make the documentation for the method easier to write, too.

if !(self.start <= self.end) {
return (0, Some(0));
}

// NOTE: the safety of `unsafe impl TrustedLen` depends on this being correct!
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),
}
}

#[inline]
fn nth(&mut self, n: usize) -> Option<A> {
if let Some(plus_n) = self.start.add_usize(n) {
if let Some(plus_n) = self.start.forward(n) {
use cmp::Ordering::*;

match plus_n.partial_cmp(&self.end) {
Some(Less) => {
self.start = plus_n.add_one();
// `plus_n + 1` should not overflow since `end` exists such that `plus_n < end`
self.start = plus_n.forward(1).expect("overflow in RangeInclusive::nth");
return Some(plus_n)
}
Some(Equal) => {
self.start.replace_one();
self.end.replace_zero();
if let Some(end_plus_one) = self.end.forward(1) {
self.start = end_plus_one
} else {
// `start == end`, and `end + 1` underflowed.
// `start - 1` overflowing would imply a type with only one valid value?
self.end = self.start.backward(1).expect("overflow in RangeInclusive::nth")
}
return Some(plus_n)
}
_ => {}
}
}

self.start.replace_one();
self.end.replace_zero();
if let Some(end_plus_one) = self.end.forward(1) {
self.start = end_plus_one
} else {
// `start == end`, and `end + 1` underflowed.
// `start - 1` overflowing would imply a type with only one valid value?
self.end = self.start.backward(1).expect("overflow in RangeInclusive::nth")
}
None
}
}
@@ -368,18 +467,32 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {

match self.start.partial_cmp(&self.end) {
Some(Less) => {
let n = self.end.sub_one();
// `end - 1` should not overflow since `start` exists such that `start < end`
let n = self.end.backward(1).expect("overflow in RangeInclusive::next_back");
Some(mem::replace(&mut self.end, n))
},
Some(Equal) => {
let last = self.end.replace_zero();
self.start.replace_one();
let last;
if let Some(start_minus_one) = self.start.backward(1) {
last = mem::replace(&mut self.end, start_minus_one);
} else {
last = self.end.clone();
// `start == end`, and `start - 1` underflowed.
// `end + 1` overflowing would imply a type with only one valid value?
self.start =
self.start.forward(1).expect("overflow in RangeInclusive::next_back");
}
Some(last)
},
_ => None,
}
}
}

#[unstable(feature = "inclusive_range",
reason = "recently added, follows RFC",
issue = "28237")]
unsafe impl<T: Step> TrustedLen for ops::RangeInclusive<T> { }

#[unstable(feature = "fused", issue = "35602")]
impl<A: Step> FusedIterator for ops::RangeInclusive<A> {}
169 changes: 131 additions & 38 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
@@ -9,8 +9,7 @@
// except according to those terms.

use core::iter::*;
use core::{i8, i16, isize};
use core::usize;
use core::{i8, i16, isize, u16, usize, i128, u128};

#[test]
fn test_lt() {
@@ -1080,20 +1079,41 @@ fn test_range() {
fn test_range_inclusive_exhaustion() {
let mut r = 10...10;
assert_eq!(r.next(), Some(10));
assert_eq!(r, 1...0);
assert_eq!(r, 11...10);
assert_eq!(r.next(), None);
assert_eq!(r, 11...10);

let mut r = 10...10;
assert_eq!(r.next_back(), Some(10));
assert_eq!(r, 10...9);
assert_eq!(r.next_back(), None);
assert_eq!(r, 10...9);

let mut r = 0_u32...0;
assert_eq!(r.next_back(), Some(0));
assert_eq!(r, 1...0);
assert_eq!(r.next_back(), None);
assert_eq!(r, 1...0);

let mut r = 10...12;
assert_eq!(r.nth(2), Some(12));
assert_eq!(r, 1...0);
assert_eq!(r, 13...12);

let mut r = 10...12;
assert_eq!(r.nth(5), None);
assert_eq!(r, 1...0);
assert_eq!(r, 13...12);

let mut r = 127_i8...127;
assert_eq!(r.next(), Some(127));
assert_eq!(r, 127...126);
assert_eq!(r.next(), None);
assert_eq!(r, 127...126);

let mut r = 255_u8...255;
assert_eq!(r.next(), Some(255));
assert_eq!(r, 255...254);
assert_eq!(r.next(), None);
assert_eq!(r, 255...254);
}

#[test]
@@ -1142,7 +1162,25 @@ fn test_range_inclusive_nth() {
assert_eq!(r.is_empty(), false);
assert_eq!(r.nth(10), None);
assert_eq!(r.is_empty(), true);
assert_eq!(r, 1...0); // We may not want to document/promise this detail
assert_eq!(r, 21...20);
}

#[test]
fn test_range_len() {
assert_eq!((0..10_u8).len(), 10);
assert_eq!((9..10_u8).len(), 1);
assert_eq!((10..10_u8).len(), 0);
assert_eq!((11..10_u8).len(), 0);
assert_eq!((100..10_u8).len(), 0);
}

#[test]
fn test_range_inclusive_len() {
assert_eq!((0...10_u8).len(), 11);
assert_eq!((9...10_u8).len(), 2);
assert_eq!((10...10_u8).len(), 1);
assert_eq!((11...10_u8).len(), 0);
assert_eq!((100...10_u8).len(), 0);
}

#[test]
@@ -1168,6 +1206,18 @@ fn test_range_step() {
assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX)));
}

#[test]
#[should_panic]
fn test_range_from_next_overflow() {
(255u8..).next();
}

#[test]
#[should_panic]
fn test_range_from_nth_overflow() {
(200u8..).nth(55);
}

#[test]
fn test_repeat() {
let mut it = repeat(42);
@@ -1251,40 +1301,83 @@ fn test_chain_fold() {
}

#[test]
fn test_step_replace_unsigned() {
let mut x = 4u32;
let y = x.replace_zero();
assert_eq!(x, 0);
assert_eq!(y, 4);
fn test_steps_between() {
assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize));
assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize));
assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize));

x = 5;
let y = x.replace_one();
assert_eq!(x, 1);
assert_eq!(y, 5);
}

#[test]
fn test_step_replace_signed() {
let mut x = 4i32;
let y = x.replace_zero();
assert_eq!(x, 0);
assert_eq!(y, 4);
assert_eq!(Step::steps_between(&20_u16, &40_100_u16), Some(40_080_usize));
assert_eq!(Step::steps_between(&-20_i16, &80_i16), Some(100_usize));
assert_eq!(Step::steps_between(&-20_030_i16, &20_050_i16), Some(40_080_usize));

x = 5;
let y = x.replace_one();
assert_eq!(x, 1);
assert_eq!(y, 5);
}
assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize));
assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize));
assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize));

#[test]
fn test_step_replace_no_between() {
let mut x = 4u128;
let y = x.replace_zero();
assert_eq!(x, 0);
assert_eq!(y, 4);
// Skip u64 / i64 to avoid dealing with 32-bit vs 64-bit platforms.

x = 5;
let y = x.replace_one();
assert_eq!(x, 1);
assert_eq!(y, 5);
assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize));
assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize));
if cfg!(target_pointer_width = "64") {
assert_eq!(Step::steps_between(&20_u128, &0x1_0000_0000_0000_0019_u128), Some(usize::MAX));
assert_eq!(Step::steps_between(&20_i128, &0x1_0000_0000_0000_0019_i128), Some(usize::MAX));
}
assert_eq!(Step::steps_between(&20_u128, &0x1_0000_0000_0000_0020_u128), None);
assert_eq!(Step::steps_between(&20_i128, &0x1_0000_0000_0000_0020_i128), None);
assert_eq!(Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128),
None);
}

#[test]
fn test_step_forward() {
assert_eq!((55_u8).forward(200_usize), Some(255_u8));
assert_eq!((252_u8).forward(200_usize), None);
assert_eq!((0_u8).forward(256_usize), None);
assert_eq!((-110_i8).forward(200_usize), Some(90_i8));
assert_eq!((-110_i8).forward(248_usize), None);
assert_eq!((-126_i8).forward(256_usize), None);

assert_eq!((35_u16).forward(100_usize), Some(135_u16));
assert_eq!((35_u16).forward(65500_usize), Some(u16::MAX));
assert_eq!((36_u16).forward(65500_usize), None);
assert_eq!((-110_i16).forward(200_usize), Some(90_i16));
assert_eq!((-20_030_i16).forward(50_050_usize), Some(30_020_i16));
assert_eq!((-10_i16).forward(40_000_usize), None);
assert_eq!((-10_i16).forward(70_000_usize), None);

assert_eq!((10_u128).forward(70_000_usize), Some(70_010_u128));
assert_eq!((10_i128).forward(70_030_usize), Some(70_020_i128));
assert_eq!((0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128).forward(0xff_usize),
Some(u128::MAX));
assert_eq!((0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128).forward(0x100_usize), None);
assert_eq!((0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128).forward(0xff_usize),
Some(i128::MAX));
assert_eq!((0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128).forward(0x100_usize), None);
}

#[test]
fn test_step_backward() {
assert_eq!((255_u8).backward(200_usize), Some(55_u8));
assert_eq!((100_u8).backward(200_usize), None);
assert_eq!((255_u8).backward(256_usize), None);
assert_eq!((90_i8).backward(200_usize), Some(-110_i8));
assert_eq!((110_i8).backward(248_usize), None);
assert_eq!((127_i8).backward(256_usize), None);

assert_eq!((135_u16).backward(100_usize), Some(35_u16));
assert_eq!((u16::MAX).backward(65500_usize), Some(35_u16));
assert_eq!((10_u16).backward(11_usize), None);
assert_eq!((90_i16).backward(200_usize), Some(-110_i16));
assert_eq!((30_020_i16).backward(50_050_usize), Some(-20_030_i16));
assert_eq!((-10_i16).backward(40_000_usize), None);
assert_eq!((-10_i16).backward(70_000_usize), None);

assert_eq!((70_010_u128).backward(70_000_usize), Some(10_u128));
assert_eq!((70_020_i128).backward(70_030_usize), Some(10_i128));
assert_eq!((10_u128).backward(7_usize), Some(3_u128));
assert_eq!((10_u128).backward(11_usize), None);
assert_eq!((-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128).backward(0xfe_usize),
Some(i128::MIN));
assert_eq!((-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128).backward(0xff_usize),
Some(i128::MIN));
}
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(iterator_step_by)]
#![feature(i128)]
#![feature(i128_type)]
#![feature(inclusive_range)]
#![feature(inclusive_range_syntax)]
24 changes: 8 additions & 16 deletions src/test/run-pass/impl-trait/example-calendar.rs
Original file line number Diff line number Diff line change
@@ -161,28 +161,20 @@ impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate {
}
}

impl std::iter::Step for NaiveDate {
unsafe impl std::iter::Step for NaiveDate {
fn steps_between(_: &Self, _: &Self) -> Option<usize> {
unimplemented!()
}

fn replace_one(&mut self) -> Self {
mem::replace(self, NaiveDate(0, 0, 1))
}

fn replace_zero(&mut self) -> Self {
mem::replace(self, NaiveDate(0, 0, 0))
}

fn add_one(&self) -> Self {
self.succ()
}

fn sub_one(&self) -> Self {
unimplemented!()
fn forward(&self, step_count: usize) -> Option<Self> {
let mut result = *self;
for _ in 0..step_count {
result = result.succ();
}
Some(result)
}

fn add_usize(&self, _: usize) -> Option<Self> {
fn backward(&self, _step_count: usize) -> Option<Self> {
unimplemented!()
}
}