From b98d16d6bf8d489923ff7737c0f1646da8888053 Mon Sep 17 00:00:00 2001 From: oberien Date: Sun, 17 Dec 2017 21:14:32 +0100 Subject: [PATCH 1/8] Add UnboundedIterator trait and implement it for simple cases --- src/liballoc/boxed.rs | 5 ++++- src/liballoc/lib.rs | 1 + src/libcore/iter/mod.rs | 40 +++++++++++++++++++++++++++++++++++++ src/libcore/iter/range.rs | 8 +++++++- src/libcore/iter/sources.rs | 5 ++++- src/libcore/iter/traits.rs | 19 ++++++++++++++++++ 6 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 6f125cdba8190..065d6a6263bfe 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -63,7 +63,7 @@ use core::borrow; use core::cmp::Ordering; use core::fmt; use core::hash::{self, Hash, Hasher}; -use core::iter::FusedIterator; +use core::iter::{FusedIterator, UnboundedIterator}; use core::marker::{self, Unsize}; use core::mem; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; @@ -759,6 +759,9 @@ impl ExactSizeIterator for Box { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Box {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Box {} + /// `FnBox` is a version of the `FnOnce` intended for use with boxed /// closure objects. The idea is that where one would normally store a diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3cc3ea467966b..04af4355e5569 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -118,6 +118,7 @@ #![feature(staged_api)] #![feature(str_internals)] #![feature(trusted_len)] +#![feature(unbounded_iter)] #![feature(unboxed_closures)] #![feature(unicode)] #![feature(unique)] diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 06c29b47bf921..18ba4c579caa6 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -331,6 +331,8 @@ pub use self::traits::{ExactSizeIterator, Sum, Product}; pub use self::traits::FusedIterator; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; +#[unstable(feature = "unbounded_iter", issue = "0")] +pub use self::traits::UnboundedIterator; mod iterator; mod range; @@ -497,6 +499,10 @@ impl FusedIterator for Rev unsafe impl TrustedLen for Rev where I: TrustedLen + DoubleEndedIterator {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Rev + where I: UnboundedIterator + DoubleEndedIterator {} + /// An iterator that clones the elements of an underlying iterator. /// /// This `struct` is created by the [`cloned`] method on [`Iterator`]. See its @@ -577,6 +583,10 @@ impl<'a, I, T: 'a> FusedIterator for Cloned where I: FusedIterator, T: Clone {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl<'a, I, T: 'a> UnboundedIterator for Cloned + where I: UnboundedIterator, T: Clone {} + #[doc(hidden)] default unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Cloned where I: TrustedRandomAccess, T: Clone @@ -648,6 +658,12 @@ impl Iterator for Cycle where I: Clone + Iterator { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Cycle where I: Clone + Iterator {} +// We can't implement `UnboundedIterator` for all `Cycle`. +// If the underlying iterator returns `None` as first element, it would +// break the contract. +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Cycle where I: Clone + UnboundedIterator {} + /// An iterator for stepping iterators by a custom amount. /// /// This `struct` is created by the [`step_by`] method on [`Iterator`]. See @@ -1175,6 +1191,10 @@ unsafe impl TrustedLen for Zip where A: TrustedLen, B: TrustedLen, {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Zip + where A: UnboundedIterator, B: UnboundedIterator {} + /// An iterator that maps the values of `iter` with `f`. /// /// This `struct` is created by the [`map`] method on [`Iterator`]. See its @@ -1317,6 +1337,11 @@ unsafe impl TrustedLen for Map where I: TrustedLen, F: FnMut(I::Item) -> B {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Map + where I: UnboundedIterator, + F: FnMut(I::Item) -> B {} + #[doc(hidden)] unsafe impl TrustedRandomAccess for Map where I: TrustedRandomAccess, @@ -1730,6 +1755,9 @@ unsafe impl TrustedLen for Enumerate where I: TrustedLen, {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Enumerate where I: UnboundedIterator {} + /// An iterator with a `peek()` that returns an optional reference to the next /// element. @@ -1845,6 +1873,9 @@ impl ExactSizeIterator for Peekable {} #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Peekable {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Peekable {} + impl Peekable { /// Returns a reference to the next() value without advancing the iterator. /// @@ -2730,6 +2761,11 @@ impl ExactSizeIterator for Fuse where I: ExactSizeIterator { } } +// Actually it's useless to call `.fuse()` on an UnboundedIterator, as the `None` +// case will never occur. Anyway, this contract still holds. +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Fuse where I: UnboundedIterator {} + /// An iterator that calls a function with a reference to each element before /// yielding it. /// @@ -2841,3 +2877,7 @@ impl ExactSizeIterator for Inspect #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Inspect where F: FnMut(&I::Item) {} + +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Inspect + where F: FnMut(&I::Item) {} diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index e9aee4a4676de..3c82cbcefa64d 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -13,7 +13,7 @@ use mem; use ops::{self, Add, Sub}; use usize; -use super::{FusedIterator, TrustedLen}; +use super::{FusedIterator, TrustedLen, UnboundedIterator}; /// Objects that can be stepped over in both directions. /// @@ -267,6 +267,12 @@ range_incl_exact_iter_impl!(u8 u16 i8 i16); 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); +// We can safely implement `UnboundedIterator` for all RangeFrom because +// `Step::add_one` will always return `Self` or diverge, which is exactly +// the contract `UnboundedIterator` describes. +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for ops::RangeFrom {} + #[stable(feature = "rust1", since = "1.0.0")] impl DoubleEndedIterator for ops::Range { #[inline] diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index b405f35d5e4db..df0c407888c11 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -12,7 +12,7 @@ use fmt; use marker; use usize; -use super::{FusedIterator, TrustedLen}; +use super::{FusedIterator, TrustedLen, UnboundedIterator}; /// An iterator that repeats an element endlessly. /// @@ -44,6 +44,9 @@ impl DoubleEndedIterator for Repeat { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Repeat {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Repeat {} + /// Creates a new iterator that endlessly repeats a single element. /// /// The `repeat()` function repeats a single value over and over and over and diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 11e668d228c48..c2b503a60a4f1 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -987,3 +987,22 @@ pub unsafe trait TrustedLen : Iterator {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl<'a, I: TrustedLen + ?Sized> TrustedLen for &'a mut I {} + +/// An iterator that will never return [`None`]. +/// +/// Any iterator implementing this trait will either continue to return +/// values infinitely, or diverge. +/// Additionally, its [`.size_hint`] must return `(usize::MAX, None)`. +/// +/// # Safety +/// +/// This trait must only be implemented when the contract is upheld. +/// +/// [`None`]: ../../std/option/enum.Option.html#variant.None +/// [`.size_hint`]: ../../std/iter/trait.Iterator.html#method.size_hint +#[unstable(feature="unbounded_iter", issue = "0")] +pub unsafe trait UnboundedIterator : Iterator {} + +#[unstable(feature="unbounded_iter", issue = "0")] +unsafe impl<'a, I: UnboundedIterator + ?Sized> UnboundedIterator for &'a mut I {} + From 3a8bb489cb1cbf52f750c630d65425c2ce91e760 Mon Sep 17 00:00:00 2001 From: oberien Date: Mon, 18 Dec 2017 00:03:39 +0100 Subject: [PATCH 2/8] Implement UnboundedIterator for Filter and SkipWhile --- src/libcore/iter/mod.rs | 77 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 18ba4c579caa6..cc4110a3ac7b1 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1394,8 +1394,7 @@ impl Iterator for Filter where P: FnMut(&I::Item) -> bool #[inline] fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate + FilterImpl::size_hint(self) } // this special case allows the compiler to make `.filter(_).count()` @@ -1482,10 +1481,43 @@ impl DoubleEndedIterator for Filter } } +// Filter specialization trait +#[doc(hidden)] +trait FilterImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General Filter impl +impl FilterImpl for Filter + where P: FnMut(&I::Item) -> bool +{ + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } +} + +// Specialized Filter impl for an underlying UnboundedIterator +impl FilterImpl for Filter + where P: FnMut(&I::Item) -> bool +{ + #[inline] + fn size_hint(&self) -> (usize, Option) { + // If the underlying iterator is unbounded, we will either filter out all items + // and thus diverge, or filter some of the infinitely many items out. + (usize::MAX, None) + } +} + #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Filter where P: FnMut(&I::Item) -> bool {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Filter + where P: FnMut(&I::Item) -> bool {} + /// An iterator that uses `f` to both filter and map elements from `iter`. /// /// This `struct` is created by the [`filter_map`] method on [`Iterator`]. See its @@ -1976,8 +2008,7 @@ impl Iterator for SkipWhile #[inline] fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate + SkipWhileImpl::size_hint(self) } #[inline] @@ -2007,10 +2038,45 @@ impl Iterator for SkipWhile } } +// SkipWhile specialization trait +#[doc(hidden)] +trait SkipWhileImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General SkipWhile impl +#[doc(hidden)] +impl SkipWhileImpl for SkipWhile + where P: FnMut(&I::Item) -> bool +{ + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } +} + +// Specialized SkipWhile impl for underlying UnboundedIterator +#[doc(hidden)] +impl SkipWhileImpl for SkipWhile + where P: FnMut(&I::Item) -> bool +{ + #[inline] + fn size_hint(&self) -> (usize, Option) { + // If the underlying iterator is unbounded, we will either skip all items + // and thus diverge, or skip the first few of the infinitely many items. + (usize::MAX, None) + } +} + #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for SkipWhile where I: FusedIterator, P: FnMut(&I::Item) -> bool {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for SkipWhile + where I: UnboundedIterator, P: FnMut(&I::Item) -> bool {} + /// An iterator that only accepts elements while `predicate` is true. /// /// This `struct` is created by the [`take_while`] method on [`Iterator`]. See its @@ -2309,6 +2375,9 @@ impl ExactSizeIterator for Take where I: ExactSizeIterator {} #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Take where I: FusedIterator {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl TrustedLen for Take {} + /// An iterator to maintain state while iterating another iterator. /// /// This `struct` is created by the [`scan`] method on [`Iterator`]. See its From 276564ebbeae650d88d156877d66404b8bfdb92f Mon Sep 17 00:00:00 2001 From: oberien Date: Mon, 18 Dec 2017 12:38:06 +0100 Subject: [PATCH 3/8] Impl UnboundedIterator for FilterMap, StepBy, Chain, Skip --- src/libcore/iter/mod.rs | 247 +++++++++++++++++++++++++++++++++---- src/libcore/iter/traits.rs | 13 +- 2 files changed, 231 insertions(+), 29 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index cc4110a3ac7b1..342a054bef8f4 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -332,7 +332,7 @@ pub use self::traits::FusedIterator; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; #[unstable(feature = "unbounded_iter", issue = "0")] -pub use self::traits::UnboundedIterator; +pub use self::traits::{UnboundedIterator, UnboundedIteratorAuto}; mod iterator; mod range; @@ -700,6 +700,20 @@ impl Iterator for StepBy where I: Iterator { #[inline] fn size_hint(&self) -> (usize, Option) { + StepByImpl::size_hint(self) + } +} + +// StepBy specialization trait +#[doc(hidden)] +trait StepByImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General StepBy impl +impl StepByImpl for StepBy { + #[inline] + default fn size_hint(&self) -> (usize, Option) { let inner_hint = self.iter.size_hint(); if self.first_take { @@ -712,12 +726,26 @@ impl Iterator for StepBy where I: Iterator { } } +// Specialized StepBy impl for an underlying UnboundedIterator +impl StepByImpl for StepBy { + #[inline] + fn size_hint(&self) -> (usize, Option) { + // We step over some of the infinitely many elements. + (usize::MAX, None) + } +} + // StepBy can only make the iterator shorter, so the len will still fit. #[unstable(feature = "iterator_step_by", reason = "unstable replacement of Range::step_by", issue = "27741")] impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} +#[unstable(feature = "iterator_step_by", + reason = "unstable replacement of Range::step_by", + issue = "27741")] +unsafe impl UnboundedIterator for StepBy {} + /// An iterator that strings two iterators together. /// /// This `struct` is created by the [`chain`] method on [`Iterator`]. See its @@ -883,17 +911,7 @@ impl Iterator for Chain where #[inline] fn size_hint(&self) -> (usize, Option) { - let (a_lower, a_upper) = self.a.size_hint(); - let (b_lower, b_upper) = self.b.size_hint(); - - let lower = a_lower.saturating_add(b_lower); - - let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) => x.checked_add(y), - _ => None - }; - - (lower, upper) + ChainImpl::size_hint(self) } } @@ -957,6 +975,64 @@ impl DoubleEndedIterator for Chain where } +// Chain specialization trait +#[doc(hidden)] +trait ChainImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General Chain impl +#[doc(hidden)] +impl ChainImpl for Chain { + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (a_lower, a_upper) = self.a.size_hint(); + let (b_lower, b_upper) = self.b.size_hint(); + + let lower = a_lower.saturating_add(b_lower); + + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => x.checked_add(y), + _ => None + }; + + (lower, upper) + } +} + +// Specialized Chain impls for underlying UnboundedIterators +// If A or B or both are unbounded, the chain will be unbounded as well. +// FIXME: #46813 +#[doc(hidden)] +impl ChainImpl for Chain + where A: UnboundedIterator, + B: Iterator, +// (A, B): UnboundedIteratorAuto +{ + #[inline] + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} +//#[doc(hidden)] +//impl ChainImpl for Chain +// where B: UnboundedIterator, (A, B): UnboundedIteratorAuto +//{ +// #[inline] +// fn size_hint(&self) -> (usize, Option) { +// (usize::MAX, None) +// } +//} +//#[doc(hidden)] +//impl ChainImpl for Chain +// where A: UnboundedIterator, B: UnboundedIterator +//{ +// #[inline] +// fn size_hint(&self) -> (usize, Option) { +// (usize::MAX, None) +// } +//} + // Note: *both* must be fused to handle double-ended iterators. #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Chain @@ -969,6 +1045,25 @@ unsafe impl TrustedLen for Chain where A: TrustedLen, B: TrustedLen, {} +// FIXME: #46813 +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Chain + where A: UnboundedIterator, + B: Iterator, +// (A, B): UnboundedIteratorAuto +{} +//#[unstable(feature = "unbounded_iter", issue = "0")] +//unsafe impl UnboundedIterator for Chain +// where A: Iterator, +// B: UnboundedIterator, +// (A, B): UnboundedIteratorAuto +//{} +//#[unstable(feature = "unbounded_iter", issue = "0")] +//unsafe impl UnboundedIterator for Chain +// where A: UnboundedIterator, +// B: UnboundedIterator +//{} + /// An iterator that iterates two other iterators simultaneously. /// /// This `struct` is created by the [`zip`] method on [`Iterator`]. See its @@ -1488,6 +1583,7 @@ trait FilterImpl { } // General Filter impl +#[doc(hidden)] impl FilterImpl for Filter where P: FnMut(&I::Item) -> bool { @@ -1499,6 +1595,7 @@ impl FilterImpl for Filter } // Specialized Filter impl for an underlying UnboundedIterator +#[doc(hidden)] impl FilterImpl for Filter where P: FnMut(&I::Item) -> bool { @@ -1560,8 +1657,7 @@ impl Iterator for FilterMap #[inline] fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate + FilterMapImpl::size_hint(self) } #[inline] @@ -1624,10 +1720,45 @@ impl DoubleEndedIterator for FilterMap } } +// FilterMap specialization trait +#[doc(hidden)] +trait FilterMapImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General FilterMap impl +#[doc(hidden)] +impl FilterMapImpl for FilterMap + where P: FnMut(I::Item) -> Option +{ + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } +} + +// Specialized FilterMap impl for an underlying UnboundedIterator +#[doc(hidden)] +impl FilterMapImpl for FilterMap + where P: FnMut(I::Item) -> Option +{ + #[inline] + fn size_hint(&self) -> (usize, Option) { + // If the underlying iterator is unbounded, we will either filter out all items + // and thus diverge, or filter some of the infinitely many items out. + (usize::MAX, None) + } +} + #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for FilterMap where F: FnMut(I::Item) -> Option {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for FilterMap + where F: FnMut(I::Item) -> Option {} + /// An iterator that yields the current count and the element during iteration. /// /// This `struct` is created by the [`enumerate`] method on [`Iterator`]. See its @@ -2224,12 +2355,7 @@ impl Iterator for Skip where I: Iterator { #[inline] fn size_hint(&self) -> (usize, Option) { - let (lower, upper) = self.iter.size_hint(); - - let lower = lower.saturating_sub(self.n); - let upper = upper.map(|x| x.saturating_sub(self.n)); - - (lower, upper) + SkipImpl::size_hint(self) } #[inline] @@ -2291,9 +2417,42 @@ impl DoubleEndedIterator for Skip where I: DoubleEndedIterator + ExactSize } } +// Skip specialization trait +#[doc(hidden)] +trait SkipImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General Skip impl +#[doc(hidden)] +impl SkipImpl for Skip { + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (lower, upper) = self.iter.size_hint(); + + let lower = lower.saturating_sub(self.n); + let upper = upper.map(|x| x.saturating_sub(self.n)); + + (lower, upper) + } +} + +// Specialized Skip impl for an underlying UnboundedIterator +#[doc(hidden)] +impl SkipImpl for Skip { + #[inline] + fn size_hint(&self) -> (usize, Option) { + // We skip the first few of the infinitely many elements. + (usize::MAX, None) + } +} + #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Skip where I: FusedIterator {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for Skip {} + /// An iterator that only iterates over the first `n` iterations of `iter`. /// /// This `struct` is created by the [`take`] method on [`Iterator`]. See its @@ -2491,13 +2650,7 @@ impl Iterator for FlatMap #[inline] fn size_hint(&self) -> (usize, Option) { - let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()); - let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()); - let lo = flo.saturating_add(blo); - match (self.iter.size_hint(), fhi, bhi) { - ((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)), - _ => (lo, None) - } + FlatMapImpl::size_hint(self) } #[inline] @@ -2601,10 +2754,50 @@ impl DoubleEndedIterator for FlatMap wher } } +// FlatMap specialization trait +#[doc(hidden)] +trait FlatMapImpl { + fn size_hint(&self) -> (usize, Option); +} + +// General StepBy impl +impl FlatMapImpl for FlatMap + where F: FnMut(I::Item) -> U, +{ + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()); + let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()); + let lo = flo.saturating_add(blo); + match (self.iter.size_hint(), fhi, bhi) { + ((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)), + _ => (lo, None) + } + } +} + +// Specialized FlatMap impl for an underlying UnboundedIterator +impl FlatMapImpl for FlatMap + where F: FnMut(I::Item) -> U, +{ + #[inline] + fn size_hint(&self) -> (usize, Option) { + // Either inner iterators return values, in which case there'll be infinitely many, + // or they don't return any, in which case we diverge. + (usize::MAX, None) + } +} #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for FlatMap where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} +#[unstable(feature = "unbounded_iter", issue = "0")] +unsafe impl UnboundedIterator for FlatMap + where I: UnboundedIterator, + U: IntoIterator, + F: FnMut(I::Item) -> U, +{} + /// An iterator that yields `None` forever after the underlying iterator /// yields `None` once. /// diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index c2b503a60a4f1..4cdaccc998bd6 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -1000,9 +1000,18 @@ unsafe impl<'a, I: TrustedLen + ?Sized> TrustedLen for &'a mut I {} /// /// [`None`]: ../../std/option/enum.Option.html#variant.None /// [`.size_hint`]: ../../std/iter/trait.Iterator.html#method.size_hint -#[unstable(feature="unbounded_iter", issue = "0")] +#[unstable(feature = "unbounded_iter", issue = "0")] pub unsafe trait UnboundedIterator : Iterator {} -#[unstable(feature="unbounded_iter", issue = "0")] +#[unstable(feature = "unbounded_iter", issue = "0")] unsafe impl<'a, I: UnboundedIterator + ?Sized> UnboundedIterator for &'a mut I {} +// Hacky auto trait to allow specialization of iter::Chain, +// because it requares either A: UI or B: UI or both. +#[unstable(feature = "unbounded_iter", issue = "0")] +#[doc(hidden)] +pub auto trait UnboundedIteratorAuto {} + +#[unstable(feature = "unbounded_iter", issue = "0")] +impl !UnboundedIteratorAuto for (A, B) + where A: UnboundedIteratorAuto, B: UnboundedIteratorAuto {} From b905bf28e7cabc7f05d84b642d18fdac6625ae1c Mon Sep 17 00:00:00 2001 From: oberien Date: Tue, 19 Dec 2017 23:39:17 +0100 Subject: [PATCH 4/8] Fix size_hint tests and add UnboundedIterator tests --- src/libcore/tests/iter.rs | 120 +++++++++++++++++++++++++++++++++++++- src/libcore/tests/lib.rs | 1 + 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 5cac5b26d88bd..f9a0c7de8ea67 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -952,14 +952,14 @@ fn test_iterator_size_hint() { assert_eq!(c.clone().take(5).size_hint(), (5, Some(5))); assert_eq!(c.clone().skip(5).size_hint().1, None); assert_eq!(c.clone().take_while(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().skip_while(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().skip_while(|_| false).size_hint(), (usize::MAX, None)); assert_eq!(c.clone().enumerate().size_hint(), (usize::MAX, None)); assert_eq!(c.clone().chain(vi.clone().cloned()).size_hint(), (usize::MAX, None)); assert_eq!(c.clone().zip(vi.clone()).size_hint(), (10, Some(10))); assert_eq!(c.clone().scan(0, |_,_| Some(0)).size_hint(), (0, None)); - assert_eq!(c.clone().filter(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().filter(|_| false).size_hint(), (usize::MAX, None)); assert_eq!(c.clone().map(|_| 0).size_hint(), (usize::MAX, None)); - assert_eq!(c.filter_map(|_| Some(0)).size_hint(), (0, None)); + assert_eq!(c.filter_map(|_| Some(0)).size_hint(), (usize::MAX, None)); assert_eq!(vi.clone().take(5).size_hint(), (5, Some(5))); assert_eq!(vi.clone().take(12).size_hint(), (10, Some(10))); @@ -1701,3 +1701,117 @@ fn test_flat_map_try_folds() { assert_eq!(iter.try_rfold(0, i8::checked_add), None); assert_eq!(iter.next_back(), Some(35)); } + +fn test_unbounded_iterator(iter: I) { + assert_eq!(iter.size_hint(), (usize::MAX, None)) +} + +fn test_trusted_len(_: I) {} + +#[test] +fn test_unbounded_iterator_repeat() { + test_unbounded_iterator(repeat(0)) +} + +#[test] +fn test_unbounded_iterator_rangefrom() { + test_unbounded_iterator(0..) +} + +#[test] +fn test_unbounded_iterator_rev() { + test_unbounded_iterator(repeat(0).rev()) +} + +#[test] +fn test_unbounded_iterator_filter() { + test_unbounded_iterator(repeat(0).filter(|_| true)) +} + +#[test] +fn test_unbounded_iterator_cycle() { + test_unbounded_iterator(repeat(0).cycle()) +} + +#[test] +fn test_unbounded_iterator_fuse() { + test_unbounded_iterator(repeat(0).fuse()) +} + +#[test] +fn test_unbounded_iterator_map() { + test_unbounded_iterator(repeat(0).map(|_| 1)) +} + +#[test] +fn test_unbounded_iterator_inspect() { + test_unbounded_iterator(repeat(0).inspect(|_| {})) +} + +#[test] +fn test_unbounded_iterator_skip_while() { + test_unbounded_iterator(repeat(0).skip_while(|_| false)) +} + +#[test] +fn test_unbounded_iterator_filter_map() { + test_unbounded_iterator(repeat(0).filter_map(|_| Some(1))) +} + +#[test] +fn test_unbounded_iterator_zip() { + test_unbounded_iterator(repeat(0).zip(0..)) +} + +#[test] +fn test_unbounded_iterator_step_by() { + test_unbounded_iterator(repeat(0).step_by(5)) +} + +#[test] +fn test_unbounded_iterator_chain() { + test_unbounded_iterator(repeat(0).chain(0..)); + test_unbounded_iterator(repeat(0).chain(Some(1))); + // FIXME: #46813 +// test_unbounded_iterator(Some(1).into_iter().chain(repeat(0))); +} + +#[test] +fn test_unbounded_iterator_enumerate() { + test_unbounded_iterator(repeat(0).enumerate()) +} + +#[test] +fn test_unbounded_iterator_flat_map() { + test_unbounded_iterator(repeat(0).flat_map(|_| Some(1))); +} + +#[test] +fn test_unbounded_iterator_skip() { + test_unbounded_iterator(repeat(0).skip(5)) +} + +#[test] +fn test_unbounded_iterator_peekable() { + test_unbounded_iterator(repeat(0).peekable()) +} + +#[test] +fn test_unbounded_iterator_cloned() { + test_unbounded_iterator(repeat(&0).cloned()) +} + +#[test] +fn test_unbounded_iterator_box() { + test_unbounded_iterator(Box::new(repeat(0))) +} + +#[test] +fn test_trusted_len_repeat_take() { + test_trusted_len(repeat(0).take(100)) +} + +#[test] +fn test_trusted_len_rangefrom_take() { + test_trusted_len((0..).take(100)) +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 0e445cdac358a..281a96f0d04df 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -38,6 +38,7 @@ #![feature(step_trait)] #![feature(test)] #![feature(trusted_len)] +#![feature(unbounded_iter)] #![feature(try_from)] #![feature(try_trait)] #![feature(unique)] From cbf23b667d79382eb1b1f370de38b996c19fa1e3 Mon Sep 17 00:00:00 2001 From: oberien Date: Fri, 22 Dec 2017 13:04:50 +0100 Subject: [PATCH 5/8] Add FIXMEs --- src/libcore/iter/mod.rs | 12 +++++++++++- src/libcore/iter/traits.rs | 15 ++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 342a054bef8f4..a0e0e01101c1a 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -332,7 +332,10 @@ pub use self::traits::FusedIterator; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; #[unstable(feature = "unbounded_iter", issue = "0")] -pub use self::traits::{UnboundedIterator, UnboundedIteratorAuto}; +pub use self::traits::UnboundedIterator; +// FIXME: #46813 +//#[unstable(feature = "unbounded_iter", issue = "0")] +//pub use self::traits::UnboundedIteratorAuto; mod iterator; mod range; @@ -705,6 +708,7 @@ impl Iterator for StepBy where I: Iterator { } // StepBy specialization trait +// FIXME: #36262 #[doc(hidden)] trait StepByImpl { fn size_hint(&self) -> (usize, Option); @@ -976,6 +980,7 @@ impl DoubleEndedIterator for Chain where } // Chain specialization trait +// FIXME: #36262 #[doc(hidden)] trait ChainImpl { fn size_hint(&self) -> (usize, Option); @@ -1577,6 +1582,7 @@ impl DoubleEndedIterator for Filter } // Filter specialization trait +// FIXME: #36262 #[doc(hidden)] trait FilterImpl { fn size_hint(&self) -> (usize, Option); @@ -1721,6 +1727,7 @@ impl DoubleEndedIterator for FilterMap } // FilterMap specialization trait +// FIXME: #36262 #[doc(hidden)] trait FilterMapImpl { fn size_hint(&self) -> (usize, Option); @@ -2170,6 +2177,7 @@ impl Iterator for SkipWhile } // SkipWhile specialization trait +// FIXME: #36262 #[doc(hidden)] trait SkipWhileImpl { fn size_hint(&self) -> (usize, Option); @@ -2418,6 +2426,7 @@ impl DoubleEndedIterator for Skip where I: DoubleEndedIterator + ExactSize } // Skip specialization trait +// FIXME: #36262 #[doc(hidden)] trait SkipImpl { fn size_hint(&self) -> (usize, Option); @@ -2755,6 +2764,7 @@ impl DoubleEndedIterator for FlatMap wher } // FlatMap specialization trait +// FIXME: #36262 #[doc(hidden)] trait FlatMapImpl { fn size_hint(&self) -> (usize, Option); diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 4cdaccc998bd6..bf96baef40425 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -1008,10 +1008,11 @@ unsafe impl<'a, I: UnboundedIterator + ?Sized> UnboundedIterator for &'a mut I { // Hacky auto trait to allow specialization of iter::Chain, // because it requares either A: UI or B: UI or both. -#[unstable(feature = "unbounded_iter", issue = "0")] -#[doc(hidden)] -pub auto trait UnboundedIteratorAuto {} - -#[unstable(feature = "unbounded_iter", issue = "0")] -impl !UnboundedIteratorAuto for (A, B) - where A: UnboundedIteratorAuto, B: UnboundedIteratorAuto {} +// FIXME: #46813 +//#[unstable(feature = "unbounded_iter", issue = "0")] +//#[doc(hidden)] +//pub auto trait UnboundedIteratorAuto {} +// +//#[unstable(feature = "unbounded_iter", issue = "0")] +//impl !UnboundedIteratorAuto for (A, B) +// where A: UnboundedIteratorAuto, B: UnboundedIteratorAuto {} From ae130605ff96763761dc0af269a6d5d60d5c7f08 Mon Sep 17 00:00:00 2001 From: oberien Date: Fri, 22 Dec 2017 15:38:21 +0100 Subject: [PATCH 6/8] Optimize Cycle to a noop --- src/libcore/iter/mod.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index a0e0e01101c1a..2d31554f336b3 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -641,6 +641,30 @@ impl Iterator for Cycle where I: Clone + Iterator { #[inline] fn next(&mut self) -> Option<::Item> { + CycleImpl::next(self) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + CycleImpl::size_hint(self) + } +} + +// Cycle specialization trait +// FIXME: #36262 +#[doc(hidden)] +trait CycleImpl { + type Item; + fn next(&mut self) -> Option; + fn size_hint(&self) -> (usize, Option); +} + +// General Cycle impl +impl CycleImpl for Cycle where I: Clone + Iterator { + type Item = ::Item; + + #[inline] + default fn next(&mut self) -> Option { match self.iter.next() { None => { self.iter = self.orig.clone(); self.iter.next() } y => y @@ -648,7 +672,7 @@ impl Iterator for Cycle where I: Clone + Iterator { } #[inline] - fn size_hint(&self) -> (usize, Option) { + default fn size_hint(&self) -> (usize, Option) { // the cycle iterator is either empty or infinite match self.orig.size_hint() { sz @ (0, Some(0)) => sz, @@ -658,6 +682,19 @@ impl Iterator for Cycle where I: Clone + Iterator { } } +// Specialized StepBy impl for an underlying UnboundedIterator +impl CycleImpl for Cycle where I: Clone + UnboundedIterator { + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} + #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Cycle where I: Clone + Iterator {} From 6125f8c7ac0b1bf72731d3fe0df389501c0c9388 Mon Sep 17 00:00:00 2001 From: oberien Date: Sat, 23 Dec 2017 11:54:16 +0100 Subject: [PATCH 7/8] Make UnboundedIterator supertrait of FusedIterator This optimizes UnboundedIterator::fuse to a noop. --- src/libcore/iter/mod.rs | 11 ++++++++--- src/libcore/iter/traits.rs | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 2d31554f336b3..fca98389cbd09 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -682,7 +682,7 @@ impl CycleImpl for Cycle where I: Clone + Iterator { } } -// Specialized StepBy impl for an underlying UnboundedIterator +// Specialized Cycle impl for an underlying UnboundedIterator impl CycleImpl for Cycle where I: Clone + UnboundedIterator { #[inline] fn next(&mut self) -> Option { @@ -782,6 +782,11 @@ impl StepByImpl for StepBy { issue = "27741")] impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} +#[unstable(feature = "iterator_step_by", + reason = "unstable replacement of Range::step_by", + issue = "27741")] +impl FusedIterator for StepBy where I: FusedIterator {} + #[unstable(feature = "iterator_step_by", reason = "unstable replacement of Range::step_by", issue = "27741")] @@ -1091,7 +1096,7 @@ unsafe impl TrustedLen for Chain #[unstable(feature = "unbounded_iter", issue = "0")] unsafe impl UnboundedIterator for Chain where A: UnboundedIterator, - B: Iterator, + B: FusedIterator, // (A, B): UnboundedIteratorAuto {} //#[unstable(feature = "unbounded_iter", issue = "0")] @@ -2807,7 +2812,7 @@ trait FlatMapImpl { fn size_hint(&self) -> (usize, Option); } -// General StepBy impl +// General FlatMap impl impl FlatMapImpl for FlatMap where F: FnMut(I::Item) -> U, { diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index bf96baef40425..afaf4879b9106 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -1000,8 +1000,10 @@ unsafe impl<'a, I: TrustedLen + ?Sized> TrustedLen for &'a mut I {} /// /// [`None`]: ../../std/option/enum.Option.html#variant.None /// [`.size_hint`]: ../../std/iter/trait.Iterator.html#method.size_hint +// We can't implement FusedIterator for T where T: UnboundedIterator due to +// a clash for &'a mut I. Thus, we need to make it a supertrait. #[unstable(feature = "unbounded_iter", issue = "0")] -pub unsafe trait UnboundedIterator : Iterator {} +pub unsafe trait UnboundedIterator : FusedIterator {} #[unstable(feature = "unbounded_iter", issue = "0")] unsafe impl<'a, I: UnboundedIterator + ?Sized> UnboundedIterator for &'a mut I {} From c88f74ac72a030d8955d62dceeb90e6b0fcc6331 Mon Sep 17 00:00:00 2001 From: oberien Date: Sat, 30 Dec 2017 23:28:36 +0100 Subject: [PATCH 8/8] Fix grammar mistakes --- src/libcore/iter/mod.rs | 2 +- src/libcore/iter/range.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index fca98389cbd09..97d8343af8e95 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1650,7 +1650,7 @@ impl FilterImpl for Filter #[inline] fn size_hint(&self) -> (usize, Option) { // If the underlying iterator is unbounded, we will either filter out all items - // and thus diverge, or filter some of the infinitely many items out. + // and thus diverge, or filter out some of the infinitely many items. (usize::MAX, None) } } diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 3c82cbcefa64d..3b12b4bf13877 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -269,7 +269,7 @@ range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64); // We can safely implement `UnboundedIterator` for all RangeFrom because // `Step::add_one` will always return `Self` or diverge, which is exactly -// the contract `UnboundedIterator` describes. +// what the contract `UnboundedIterator` describes. #[unstable(feature = "unbounded_iter", issue = "0")] unsafe impl UnboundedIterator for ops::RangeFrom {}