mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #43077 - SimonSapin:ranges, r=alexcrichton
Implement O(1)-time Iterator::nth for Range*, and slim the Step trait Fixes #43064. Fixes part of #39975. Fixes items 1 <s>and 3</s> of #42168. CC #27741. I think #42310 and #43012 should not have landed without the `nth` part of this PR, but oh well.
This commit is contained in:
commit
4d4d76cf42
@ -8,6 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use convert::TryFrom;
|
||||
use mem;
|
||||
use ops::{self, Add, Sub};
|
||||
use usize;
|
||||
@ -21,22 +22,13 @@ use super::{FusedIterator, TrustedLen};
|
||||
#[unstable(feature = "step_trait",
|
||||
reason = "likely to be replaced by finer-grained traits",
|
||||
issue = "42168")]
|
||||
pub trait Step: PartialOrd + Sized {
|
||||
/// Steps `self` if possible.
|
||||
fn step(&self, by: &Self) -> Option<Self>;
|
||||
|
||||
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`.
|
||||
///
|
||||
/// Returns `None` if it is not possible to calculate `steps_between`
|
||||
/// without overflow.
|
||||
fn steps_between(start: &Self, end: &Self, by: &Self) -> Option<usize>;
|
||||
|
||||
/// Same as `steps_between`, but with a `by` of 1
|
||||
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize>;
|
||||
|
||||
/// Tests whether this step is negative or not (going backwards)
|
||||
fn is_negative(&self) -> bool;
|
||||
fn steps_between(start: &Self, end: &Self) -> Option<usize>;
|
||||
|
||||
/// Replaces this step with `1`, returning itself
|
||||
fn replace_one(&mut self) -> Self;
|
||||
@ -49,6 +41,34 @@ pub trait Step: PartialOrd + Sized {
|
||||
|
||||
/// Subtracts one to this step, returning the result
|
||||
fn sub_one(&self) -> Self;
|
||||
|
||||
/// Add an usize, returning None on overflow
|
||||
fn add_usize(&self, n: 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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace_zero(&mut self) -> Self {
|
||||
mem::replace(self, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_one(&self) -> Self {
|
||||
Add::add(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_one(&self) -> Self {
|
||||
Sub::sub(*self, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! step_impl_unsigned {
|
||||
@ -57,128 +77,67 @@ macro_rules! step_impl_unsigned {
|
||||
reason = "likely to be replaced by finer-grained traits",
|
||||
issue = "42168")]
|
||||
impl Step for $t {
|
||||
#[inline]
|
||||
fn step(&self, by: &$t) -> Option<$t> {
|
||||
(*self).checked_add(*by)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
|
||||
if *by == 0 { return None; }
|
||||
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
|
||||
if *start < *end {
|
||||
// Note: We assume $t <= usize here
|
||||
let diff = (*end - *start) as usize;
|
||||
let by = *by as usize;
|
||||
if diff % by > 0 {
|
||||
Some(diff / by + 1)
|
||||
} else {
|
||||
Some(diff / by)
|
||||
}
|
||||
Some((*end - *start) as usize)
|
||||
} else {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_negative(&self) -> bool {
|
||||
false
|
||||
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 replace_one(&mut self) -> Self {
|
||||
mem::replace(self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace_zero(&mut self) -> Self {
|
||||
mem::replace(self, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_one(&self) -> Self {
|
||||
Add::add(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_one(&self) -> Self {
|
||||
Sub::sub(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
|
||||
Self::steps_between(start, end, &1)
|
||||
}
|
||||
step_identical_methods!();
|
||||
}
|
||||
)*)
|
||||
}
|
||||
macro_rules! step_impl_signed {
|
||||
($($t:ty)*) => ($(
|
||||
($( [$t:ty : $unsigned:ty] )*) => ($(
|
||||
#[unstable(feature = "step_trait",
|
||||
reason = "likely to be replaced by finer-grained traits",
|
||||
issue = "42168")]
|
||||
impl Step for $t {
|
||||
#[inline]
|
||||
fn step(&self, by: &$t) -> Option<$t> {
|
||||
(*self).checked_add(*by)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
|
||||
if *by == 0 { return None; }
|
||||
let diff: usize;
|
||||
let by_u: usize;
|
||||
if *by > 0 {
|
||||
if *start >= *end {
|
||||
return Some(0);
|
||||
}
|
||||
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.
|
||||
diff = (*end as isize).wrapping_sub(*start as isize) as usize;
|
||||
by_u = *by as usize;
|
||||
Some((*end as isize).wrapping_sub(*start as isize) as usize)
|
||||
} else {
|
||||
if *start <= *end {
|
||||
return Some(0);
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
diff = (*start as isize).wrapping_sub(*end as isize) as usize;
|
||||
by_u = (*by as isize).wrapping_mul(-1) as usize;
|
||||
}
|
||||
if diff % by_u > 0 {
|
||||
Some(diff / by_u + 1)
|
||||
} else {
|
||||
Some(diff / by_u)
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_negative(&self) -> bool {
|
||||
*self < 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace_one(&mut self) -> Self {
|
||||
mem::replace(self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace_zero(&mut self) -> Self {
|
||||
mem::replace(self, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_one(&self) -> Self {
|
||||
Add::add(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_one(&self) -> Self {
|
||||
Sub::sub(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
|
||||
Self::steps_between(start, end, &1)
|
||||
}
|
||||
step_identical_methods!();
|
||||
}
|
||||
)*)
|
||||
}
|
||||
@ -190,54 +149,26 @@ macro_rules! step_impl_no_between {
|
||||
issue = "42168")]
|
||||
impl Step for $t {
|
||||
#[inline]
|
||||
fn step(&self, by: &$t) -> Option<$t> {
|
||||
(*self).checked_add(*by)
|
||||
}
|
||||
#[inline]
|
||||
fn steps_between(_a: &$t, _b: &$t, _by: &$t) -> Option<usize> {
|
||||
fn steps_between(_start: &Self, _end: &Self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unused_comparisons)]
|
||||
fn is_negative(&self) -> bool {
|
||||
*self < 0
|
||||
fn add_usize(&self, n: usize) -> Option<Self> {
|
||||
self.checked_add(n as $t)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace_one(&mut self) -> Self {
|
||||
mem::replace(self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace_zero(&mut self) -> Self {
|
||||
mem::replace(self, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_one(&self) -> Self {
|
||||
Add::add(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_one(&self) -> Self {
|
||||
Sub::sub(*self, 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
|
||||
Self::steps_between(start, end, &1)
|
||||
}
|
||||
step_identical_methods!();
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
step_impl_unsigned!(usize u8 u16 u32);
|
||||
step_impl_signed!(isize i8 i16 i32);
|
||||
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);
|
||||
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"))]
|
||||
@ -277,9 +208,7 @@ macro_rules! range_incl_trusted_len_impl {
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A: Step> Iterator for ops::Range<A> where
|
||||
for<'a> &'a A: Add<&'a A, Output = A>
|
||||
{
|
||||
impl<A: Step> Iterator for ops::Range<A> {
|
||||
type Item = A;
|
||||
|
||||
#[inline]
|
||||
@ -295,11 +224,24 @@ impl<A: Step> Iterator for ops::Range<A> where
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match Step::steps_between_by_one(&self.start, &self.end) {
|
||||
match Step::steps_between(&self.start, &self.end) {
|
||||
Some(hint) => (hint, Some(hint)),
|
||||
None => (0, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nth(&mut self, n: usize) -> Option<A> {
|
||||
if let Some(plus_n) = self.start.add_usize(n) {
|
||||
if plus_n < self.end {
|
||||
self.start = plus_n.add_one();
|
||||
return Some(plus_n)
|
||||
}
|
||||
}
|
||||
|
||||
self.start = self.end.clone();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// These macros generate `ExactSizeIterator` impls for various range types.
|
||||
@ -317,10 +259,7 @@ 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);
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A: Step + Clone> DoubleEndedIterator for ops::Range<A> where
|
||||
for<'a> &'a A: Add<&'a A, Output = A>,
|
||||
for<'a> &'a A: Sub<&'a A, Output = A>
|
||||
{
|
||||
impl<A: Step> DoubleEndedIterator for ops::Range<A> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<A> {
|
||||
if self.start < self.end {
|
||||
@ -333,13 +272,10 @@ impl<A: Step + Clone> DoubleEndedIterator for ops::Range<A> where
|
||||
}
|
||||
|
||||
#[unstable(feature = "fused", issue = "35602")]
|
||||
impl<A> FusedIterator for ops::Range<A>
|
||||
where A: Step, for<'a> &'a A: Add<&'a A, Output = A> {}
|
||||
impl<A: Step> FusedIterator for ops::Range<A> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A: Step> Iterator for ops::RangeFrom<A> where
|
||||
for<'a> &'a A: Add<&'a A, Output = A>
|
||||
{
|
||||
impl<A: Step> Iterator for ops::RangeFrom<A> {
|
||||
type Item = A;
|
||||
|
||||
#[inline]
|
||||
@ -353,16 +289,20 @@ impl<A: Step> Iterator for ops::RangeFrom<A> where
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::MAX, None)
|
||||
}
|
||||
|
||||
#[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();
|
||||
Some(plus_n)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "fused", issue = "35602")]
|
||||
impl<A> FusedIterator for ops::RangeFrom<A>
|
||||
where A: Step, for<'a> &'a A: Add<&'a A, Output = A> {}
|
||||
impl<A: Step> FusedIterator for ops::RangeFrom<A> {}
|
||||
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
impl<A: Step> Iterator for ops::RangeInclusive<A> where
|
||||
for<'a> &'a A: Add<&'a A, Output = A>
|
||||
{
|
||||
impl<A: Step> Iterator for ops::RangeInclusive<A> {
|
||||
type Item = A;
|
||||
|
||||
#[inline]
|
||||
@ -389,18 +329,39 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> where
|
||||
return (0, Some(0));
|
||||
}
|
||||
|
||||
match Step::steps_between_by_one(&self.start, &self.end) {
|
||||
match Step::steps_between(&self.start, &self.end) {
|
||||
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
|
||||
None => (0, None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nth(&mut self, n: usize) -> Option<A> {
|
||||
if let Some(plus_n) = self.start.add_usize(n) {
|
||||
use cmp::Ordering::*;
|
||||
|
||||
match plus_n.partial_cmp(&self.end) {
|
||||
Some(Less) => {
|
||||
self.start = plus_n.add_one();
|
||||
return Some(plus_n)
|
||||
}
|
||||
Some(Equal) => {
|
||||
self.start.replace_one();
|
||||
self.end.replace_zero();
|
||||
return Some(plus_n)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.start.replace_one();
|
||||
self.end.replace_zero();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> where
|
||||
for<'a> &'a A: Add<&'a A, Output = A>,
|
||||
for<'a> &'a A: Sub<&'a A, Output = A>
|
||||
{
|
||||
impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<A> {
|
||||
use cmp::Ordering::*;
|
||||
@ -421,5 +382,4 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> where
|
||||
}
|
||||
|
||||
#[unstable(feature = "fused", issue = "35602")]
|
||||
impl<A> FusedIterator for ops::RangeInclusive<A>
|
||||
where A: Step, for<'a> &'a A: Add<&'a A, Output = A> {}
|
||||
impl<A: Step> FusedIterator for ops::RangeInclusive<A> {}
|
||||
|
@ -1076,6 +1076,75 @@ fn test_range() {
|
||||
(isize::MAX as usize + 2, Some(isize::MAX as usize + 2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_inclusive_exhaustion() {
|
||||
let mut r = 10...10;
|
||||
assert_eq!(r.next(), Some(10));
|
||||
assert_eq!(r, 1...0);
|
||||
|
||||
let mut r = 10...10;
|
||||
assert_eq!(r.next_back(), Some(10));
|
||||
assert_eq!(r, 1...0);
|
||||
|
||||
let mut r = 10...12;
|
||||
assert_eq!(r.nth(2), Some(12));
|
||||
assert_eq!(r, 1...0);
|
||||
|
||||
let mut r = 10...12;
|
||||
assert_eq!(r.nth(5), None);
|
||||
assert_eq!(r, 1...0);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_nth() {
|
||||
assert_eq!((10..15).nth(0), Some(10));
|
||||
assert_eq!((10..15).nth(1), Some(11));
|
||||
assert_eq!((10..15).nth(4), Some(14));
|
||||
assert_eq!((10..15).nth(5), None);
|
||||
|
||||
let mut r = 10..20;
|
||||
assert_eq!(r.nth(2), Some(12));
|
||||
assert_eq!(r, 13..20);
|
||||
assert_eq!(r.nth(2), Some(15));
|
||||
assert_eq!(r, 16..20);
|
||||
assert_eq!(r.nth(10), None);
|
||||
assert_eq!(r, 20..20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_from_nth() {
|
||||
assert_eq!((10..).nth(0), Some(10));
|
||||
assert_eq!((10..).nth(1), Some(11));
|
||||
assert_eq!((10..).nth(4), Some(14));
|
||||
|
||||
let mut r = 10..;
|
||||
assert_eq!(r.nth(2), Some(12));
|
||||
assert_eq!(r, 13..);
|
||||
assert_eq!(r.nth(2), Some(15));
|
||||
assert_eq!(r, 16..);
|
||||
assert_eq!(r.nth(10), Some(26));
|
||||
assert_eq!(r, 27..);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_inclusive_nth() {
|
||||
assert_eq!((10...15).nth(0), Some(10));
|
||||
assert_eq!((10...15).nth(1), Some(11));
|
||||
assert_eq!((10...15).nth(5), Some(15));
|
||||
assert_eq!((10...15).nth(6), None);
|
||||
|
||||
let mut r = 10_u8...20;
|
||||
assert_eq!(r.nth(2), Some(12));
|
||||
assert_eq!(r, 13...20);
|
||||
assert_eq!(r.nth(2), Some(15));
|
||||
assert_eq!(r, 16...20);
|
||||
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
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_step() {
|
||||
#![allow(deprecated)]
|
||||
|
@ -18,12 +18,14 @@
|
||||
#![feature(core_private_diy_float)]
|
||||
#![feature(dec2flt)]
|
||||
#![feature(decode_utf8)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(fixed_size_array)]
|
||||
#![feature(flt2dec)]
|
||||
#![feature(fmt_internals)]
|
||||
#![feature(iterator_step_by)]
|
||||
#![feature(i128_type)]
|
||||
#![feature(inclusive_range)]
|
||||
#![feature(inclusive_range_syntax)]
|
||||
#![feature(iter_rfind)]
|
||||
#![feature(libc)]
|
||||
#![feature(nonzero)]
|
||||
|
@ -18,7 +18,6 @@ pub fn main() {
|
||||
// Bool => does not implement iterator.
|
||||
for i in false..true {}
|
||||
//~^ ERROR `bool: std::iter::Step` is not satisfied
|
||||
//~^^ ERROR `for<'a> &'a bool: std::ops::Add` is not satisfied
|
||||
|
||||
// Unsized type.
|
||||
let arr: &[_] = &[1, 2, 3];
|
||||
|
@ -162,22 +162,10 @@ impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate {
|
||||
}
|
||||
|
||||
impl std::iter::Step for NaiveDate {
|
||||
fn step(&self, by: &Self) -> Option<Self> {
|
||||
Some(self + by)
|
||||
}
|
||||
|
||||
fn steps_between(_: &Self, _: &Self, _: &Self) -> Option<usize> {
|
||||
fn steps_between(_: &Self, _: &Self) -> Option<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn steps_between_by_one(_: &Self, _: &Self) -> Option<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_negative(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn replace_one(&mut self) -> Self {
|
||||
mem::replace(self, NaiveDate(0, 0, 1))
|
||||
}
|
||||
@ -193,6 +181,10 @@ impl std::iter::Step for NaiveDate {
|
||||
fn sub_one(&self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn add_usize(&self, _: usize) -> Option<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
|
Loading…
Reference in New Issue
Block a user