diff --git a/library/core/src/iter/adapters/copied.rs b/library/core/src/iter/adapters/copied.rs index e5f2886dcafad..c2f9d98b4366b 100644 --- a/library/core/src/iter/adapters/copied.rs +++ b/library/core/src/iter/adapters/copied.rs @@ -1,7 +1,7 @@ use crate::iter::adapters::{ zip::try_get_unchecked, TrustedRandomAccess, TrustedRandomAccessNoCoerce, }; -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, PeekableIterator, TrustedLen}; use crate::ops::Try; /// An iterator that copies the elements of an underlying iterator. @@ -139,6 +139,17 @@ where } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl<'a, I, T: 'a> PeekableIterator for Copied +where + I: PeekableIterator, + T: Copy, +{ + fn peek(&self) -> Option { + self.it.peek().map(|x| *x) + } +} + #[stable(feature = "iter_copied", since = "1.36.0")] impl<'a, I, T: 'a> FusedIterator for Copied where diff --git a/library/core/src/iter/adapters/cycle.rs b/library/core/src/iter/adapters/cycle.rs index 02b5939072ef0..0de1c40e46e1f 100644 --- a/library/core/src/iter/adapters/cycle.rs +++ b/library/core/src/iter/adapters/cycle.rs @@ -1,4 +1,7 @@ -use crate::{iter::FusedIterator, ops::Try}; +use crate::{ + iter::{FusedIterator, PeekableIterator}, + ops::Try, +}; /// An iterator that repeats endlessly. /// @@ -104,5 +107,15 @@ where // and we can't do anything better than the default. } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Cycle +where + I: Clone + PeekableIterator, +{ + fn peek(&self) -> Option { + self.iter.peek().or_else(|| self.orig.peek()) + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Cycle where I: Clone + Iterator {} diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index 84e4618844a61..ae248d1d55406 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -1,7 +1,7 @@ use crate::iter::adapters::{ zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, }; -use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen}; +use crate::iter::{FusedIterator, InPlaceIterable, PeekableIterator, TrustedLen}; use crate::ops::Try; /// An iterator that yields the current count and the element during iteration. @@ -229,6 +229,16 @@ where } } +#[stable(feature = "rust1", since = "1.0.0")] +impl PeekableIterator for Enumerate +where + I: PeekableIterator, +{ + fn peek(&self) -> Option<(usize, I::Item)> { + self.iter.peek().map(|x| (self.count, x)) + } +} + #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Enumerate where I: TrustedRandomAccess {} diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs index fbf752c6f2024..5172af34f0caa 100644 --- a/library/core/src/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -1,8 +1,8 @@ use crate::intrinsics; use crate::iter::adapters::zip::try_get_unchecked; use crate::iter::{ - DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccess, - TrustedRandomAccessNoCoerce, + DoubleEndedIterator, ExactSizeIterator, FusedIterator, PeekableIterator, TrustedLen, + TrustedRandomAccess, TrustedRandomAccessNoCoerce, }; use crate::ops::Try; @@ -26,6 +26,16 @@ impl Fuse { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Fuse +where + I: PeekableIterator, +{ + fn peek(&self) -> Option { + self.iter.as_ref().and_then(|iter| iter.peek()) + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Fuse where I: Iterator {} diff --git a/library/core/src/iter/adapters/inspect.rs b/library/core/src/iter/adapters/inspect.rs index 19839fdfe5bc3..01465d0b6ad13 100644 --- a/library/core/src/iter/adapters/inspect.rs +++ b/library/core/src/iter/adapters/inspect.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable}; +use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, PeekableIterator}; use crate::ops::Try; /// An iterator that calls a function with a reference to each element before @@ -145,6 +145,16 @@ where } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Inspect +where + F: FnMut(&I::Item), +{ + fn peek(&self) -> Option { + self.iter.peek() + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Inspect where F: FnMut(&I::Item) {} diff --git a/library/core/src/iter/adapters/take.rs b/library/core/src/iter/adapters/take.rs index 2962e0104d11d..e65dd53f0fb3c 100644 --- a/library/core/src/iter/adapters/take.rs +++ b/library/core/src/iter/adapters/take.rs @@ -1,5 +1,7 @@ use crate::cmp; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen}; +use crate::iter::{ + adapters::SourceIter, FusedIterator, InPlaceIterable, PeekableIterator, TrustedLen, +}; use crate::ops::{ControlFlow, Try}; /// An iterator that only iterates over the first `n` iterations of `iter`. @@ -237,6 +239,16 @@ where #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Take where I: ExactSizeIterator {} +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Take +where + I: PeekableIterator, +{ + fn peek(&self) -> Option { + if self.n == 0 { None } else { self.iter.peek() } + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Take where I: FusedIterator {} diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 145c5ee109d7f..9dd04ce48ab5e 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -382,6 +382,8 @@ pub use self::sources::{successors, Successors}; pub use self::traits::FusedIterator; #[unstable(issue = "none", feature = "inplace_iteration")] pub use self::traits::InPlaceIterable; +#[unstable(feature = "peekable_iterator", issue = "none")] +pub use self::traits::PeekableIterator; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; #[unstable(feature = "trusted_step", issue = "85731")] diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 0ae94c05da658..f3b89af25f1b6 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -4,7 +4,8 @@ use crate::mem; use crate::ops::{self, Try}; use super::{ - FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep, + FusedIterator, PeekableIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, + TrustedStep, }; // Safety: All invariants are upheld. @@ -765,6 +766,13 @@ impl Iterator for ops::Range { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for ops::Range { + fn peek(&self) -> Option { + if self.start < self.end { Some(self.start.clone()) } else { None } + } +} + // These macros generate `ExactSizeIterator` impls for various range types. // // * `ExactSizeIterator::len` is required to always return an exact `usize`, @@ -879,6 +887,13 @@ impl Iterator for ops::RangeFrom { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for ops::RangeFrom { + fn peek(&self) -> Option { + Some(self.start.clone()) + } +} + // Safety: See above implementation for `ops::Range` #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for ops::RangeFrom {} @@ -1186,6 +1201,13 @@ impl Iterator for ops::RangeInclusive { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for ops::RangeInclusive { + fn peek(&self) -> Option { + if self.is_empty() { None } else { Some(self.start.clone()) } + } +} + #[stable(feature = "inclusive_range", since = "1.26.0")] impl DoubleEndedIterator for ops::RangeInclusive { #[inline] diff --git a/library/core/src/iter/sources/empty.rs b/library/core/src/iter/sources/empty.rs index 98734c527f2b9..b62ad135120e1 100644 --- a/library/core/src/iter/sources/empty.rs +++ b/library/core/src/iter/sources/empty.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, PeekableIterator, TrustedLen}; use crate::marker; /// Creates an iterator that yields nothing. @@ -68,6 +68,13 @@ impl ExactSizeIterator for Empty { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Empty { + fn peek(&self) -> Option { + None + } +} + #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Empty {} diff --git a/library/core/src/iter/sources/once.rs b/library/core/src/iter/sources/once.rs index 6e9ed0d3c5278..c95a1613fe8f6 100644 --- a/library/core/src/iter/sources/once.rs +++ b/library/core/src/iter/sources/once.rs @@ -1,4 +1,4 @@ -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, PeekableIterator, TrustedLen}; /// Creates an iterator that yields an element exactly once. /// @@ -92,6 +92,13 @@ impl ExactSizeIterator for Once { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Once { + fn peek(&self) -> Option { + self.inner.peek() + } +} + #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Once {} diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index 733142ed01103..df05411ffc604 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -1,4 +1,4 @@ -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, PeekableIterator, TrustedLen}; /// Creates a new iterator that endlessly repeats a single element. /// @@ -122,6 +122,13 @@ impl DoubleEndedIterator for Repeat { } } +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for Repeat { + fn peek(&self) -> Option { + Some(self.element) + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Repeat {} diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index ed0fb634dbf05..344aa86a102fb 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -4,6 +4,7 @@ mod double_ended; mod exact_size; mod iterator; mod marker; +mod peekable; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ @@ -19,3 +20,5 @@ pub use self::{ pub use self::marker::InPlaceIterable; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::marker::TrustedStep; +#[unstable(feature = "peekable_iterator", issue = "none")] +pub use self::peekable::PeekableIterator; diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs new file mode 100644 index 0000000000000..11f766e7948e7 --- /dev/null +++ b/library/core/src/iter/traits/peekable.rs @@ -0,0 +1,101 @@ +/// An iterator whose elements may be checked without advancing the iterator. +/// +/// In general, many [`Iterator`]s are able to "peek" their next element, +/// showing what would be returned by a call to `next` without advancing their +/// internal state. If these iterators do not offer such functionality, it can +/// be manually added to an iterator using the [`peekable`] method. +/// +/// In most cases, calling [`peekable`] on a [`PeekableIterator`] shouldn't +/// noticeably affect functionality, however, it's worth pointing out a few +/// differences between [`peekable`] and [`PeekableIterator`]: +/// +/// * Stateful iterators like those using [`inspect`](Iterator::inspect) will +/// eagerly evaluate when peeked by a [`peekable`] wrapper, but may do so +/// lazily with a custom [`PeekableIterator`] implementation. +/// * The [`peekable`] wrapper will incur a small performance penalty for +/// [`next`] and [`next_back`], but [`PeekableIterator`] implementations +/// incur no such penalty. +/// * The [`peekable`] wrapper will return a reference to its item, whereas +/// [`PeekableIterator`] will return the item directly. +/// +/// Note that this trait is a safe trait and as such does *not* and *cannot* +/// guarantee that the peeked value will be returned in a subsequent call to +/// [`next`], no matter how soon the two are called together. A common +/// example of this is interior mutability; if the interior state of the +/// iterator is mutated between a call to [`peek`] and [`next`], then the +/// values may differ. +/// +/// [`peek`]: Self::peek +/// [`peekable`]: Iterator::peekable +/// [`Peekable`]: super::Peekable +/// [`next`]: Iterator::next +/// [`next_back`]: DoubleEndedIterator::next_back +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// #![feature(peekable_iterator)] +/// use std::iter::PeekableIterator; +/// +/// // a range knows its current state exactly +/// let five = 0..5; +/// +/// assert_eq!(Some(0), five.peek()); +/// ``` +#[unstable(feature = "peekable_iterator", issue = "none")] +pub trait PeekableIterator: Iterator { + /// Returns a reference to the [`next`] value without advancing the iterator. + /// + /// [`next`]: Iterator::next + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(peekable_iterator)] + /// use std::iter::PeekableIterator; + /// + /// // a finite range knows its current state exactly + /// let five = 0..5; + /// + /// assert_eq!(Some(0), five.peek()); + /// ``` + #[unstable(feature = "peekable_iterator", issue = "none")] + fn peek(&self) -> Option; + + /// Returns `true` if the [`next`] value is `None`. + /// + /// [`next`]: Iterator::next + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(peekable_iterator)] + /// use std::iter::PeekableIterator; + /// + /// let mut one_element = std::iter::once(0); + /// assert!(one_element.has_next()); + /// + /// assert_eq!(one_element.next(), Some(0)); + /// assert!(!one_element.has_next()); + /// + /// assert_eq!(one_element.next(), None); + /// ``` + #[inline] + #[unstable(feature = "peekable_iterator", issue = "none")] + fn has_next(&self) -> bool { + self.peek().is_some() + } +} + +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for &mut I { + fn peek(&self) -> Option { + (**self).peek() + } +} diff --git a/library/core/src/option.rs b/library/core/src/option.rs index bcba18a4a3e0a..afe30c17bb794 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -502,7 +502,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; +use crate::iter::{self, FromIterator, FusedIterator, PeekableIterator, TrustedLen}; use crate::panicking::{panic, panic_str}; use crate::pin::Pin; use crate::{ @@ -2043,6 +2043,12 @@ impl DoubleEndedIterator for Item { } } +impl PeekableIterator for Item { + fn peek(&self) -> Option { + self.opt.clone() + } +} + impl ExactSizeIterator for Item {} impl FusedIterator for Item {} unsafe impl TrustedLen for Item {} @@ -2083,6 +2089,13 @@ impl<'a, A> DoubleEndedIterator for Iter<'a, A> { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Iter<'_, A> {} +#[unstable(feature = "peekable_iterator", issue = "none")] +impl<'a, A> PeekableIterator for Iter<'a, A> { + fn peek(&self) -> Option<&'a A> { + self.inner.peek() + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Iter<'_, A> {} @@ -2174,6 +2187,17 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} +#[unstable(feature = "peekable_iterator", issue = "none")] +impl PeekableIterator for IntoIter { + fn peek(&self) -> Option { + self.inner.peek() + } + + fn has_next(&self) -> bool { + self.inner.has_next() + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {}