diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index fdae5c08f1e8e..f28cff9762f90 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -1,7 +1,7 @@ //! Defines the `IntoIter` owned iterator for arrays. use crate::intrinsics::transmute_unchecked; -use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}; +use crate::iter::{FusedIterator, PeekableIterator, TrustedLen, TrustedRandomAccessNoCoerce}; use crate::mem::MaybeUninit; use crate::num::NonZero; use crate::ops::{IndexRange, Range, Try}; @@ -350,6 +350,13 @@ impl FusedIterator for IntoIter {} #[stable(feature = "array_value_iter_impls", since = "1.40.0")] unsafe impl TrustedLen for IntoIter {} +#[unstable(feature = "peekable_iterator", issue = "132973")] +impl PeekableIterator for IntoIter { + fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> U) -> U { + self.unsize_mut().peek_with(func) + } +} + #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] #[rustc_unsafe_specialization_marker] diff --git a/library/core/src/array/iter/iter_inner.rs b/library/core/src/array/iter/iter_inner.rs index 3c2343591f8cf..769be779a139d 100644 --- a/library/core/src/array/iter/iter_inner.rs +++ b/library/core/src/array/iter/iter_inner.rs @@ -175,6 +175,19 @@ impl PolymorphicIter<[MaybeUninit]> { }) } + #[inline] + pub(super) fn peek_with(&self, func: impl for<'b> FnOnce(Option<&'b T>) -> U) -> U { + let tmp = self.alive.clone().next().map(|idx| { + // SAFETY: `idx` is in self.alive range + unsafe { self.data.get_unchecked(idx).assume_init_read() } + }); + + let out = func(tmp.as_ref()); + // Avoid dropping before the item is consumed + crate::mem::forget(tmp); + out + } + #[inline] pub(super) fn size_hint(&self) -> (usize, Option) { let len = self.len(); diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index a6522659620a0..5ef1e1ac1758d 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -1,5 +1,5 @@ use crate::iter::adapters::SourceIter; -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, PeekableIterator, TrustedLen}; use crate::ops::{ControlFlow, Try}; /// An iterator with a `peek()` that returns an optional reference to the next @@ -322,6 +322,14 @@ impl Peekable { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Peekable where I: TrustedLen {} +#[unstable(feature = "peekable_iterator", issue = "132973")] +impl PeekableIterator for Peekable { + fn peek_with(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T { + let tmp = self.peek(); + func(tmp) + } +} + #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for Peekable where diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 56ca1305b601b..fa1564712ed35 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -452,6 +452,8 @@ pub use self::traits::FusedIterator; pub use self::traits::InPlaceIterable; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::Iterator; +#[unstable(feature = "peekable_iterator", issue = "132973")] +pub use self::traits::PeekableIterator; #[unstable(issue = "none", feature = "trusted_fused")] pub use self::traits::TrustedFused; #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index b330e9ffe21ac..116353c9a1783 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; mod unchecked_iterator; #[unstable(issue = "none", feature = "inplace_iteration")] @@ -12,6 +13,8 @@ pub use self::marker::InPlaceIterable; pub use self::marker::TrustedFused; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::marker::TrustedStep; +#[unstable(feature = "peekable_iterator", issue = "132973")] +pub use self::peekable::PeekableIterator; pub(crate) use self::unchecked_iterator::UncheckedIterator; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs new file mode 100644 index 0000000000000..0de6d98122630 --- /dev/null +++ b/library/core/src/iter/traits/peekable.rs @@ -0,0 +1,28 @@ +#[unstable(feature = "peekable_iterator", issue = "132973")] +/// Iterators which inherently support peeking without needing to be wrapped by a `Peekable`. +pub trait PeekableIterator: Iterator { + /// Executes the closure with a reference to the `next()` value without advancing the iterator. + fn peek_with(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T; + + /// Executes the closure on the `next()` element without advancing the iterator, or returns `None` if the iterator is exhausted. + fn peek_map(&mut self, func: impl for<'a> FnOnce(&'a Self::Item) -> T) -> Option { + self.peek_with(|x| x.map(|y| func(y))) + } + + /// Returns the `next()` element if the given predicate holds true. + fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { + match self.peek_map(func) { + Some(true) => self.next(), + _ => None, + } + } + + /// Moves forward and return the `next()` item if it is equal to the expected value. + fn next_if_eq(&mut self, expected: &T) -> Option + where + Self::Item: PartialEq, + T: ?Sized, + { + self.next_if(|x| x == expected) + } +} diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index ae910e0525209..a4708c8602d8a 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -6,7 +6,8 @@ mod macros; use super::{from_raw_parts, from_raw_parts_mut}; use crate::hint::assert_unchecked; use crate::iter::{ - FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, UncheckedIterator, + FusedIterator, PeekableIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, + UncheckedIterator, }; use crate::marker::PhantomData; use crate::mem::{self, SizedTypeProperties}; diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 7c1ed3fe8a246..df2c2945dd51c 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -468,6 +468,34 @@ macro_rules! iterator { } } + #[unstable(feature = "peekable_iterator", issue = "132973")] + impl<'a, T> PeekableIterator for $name<'a, T> { + fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> U) -> U { + let ptr = self.ptr; + let end_or_len = self.end_or_len; + + // SAFETY: See inner comments. + unsafe { + if T::IS_ZST { + let len = end_or_len.addr(); + if len == 0 { + return func(None); + } + } else { + // SAFETY: by type invariant, the `end_or_len` field is always + // non-null for a non-ZST pointee. (This transmute ensures we + // get `!nonnull` metadata on the load of the field.) + if ptr == crate::intrinsics::transmute::<$ptr, NonNull>(end_or_len) { + return func(None); + } + } + // SAFETY: Now that we know it wasn't empty + // we can give out a reference to it. + let tmp = {ptr}.$into_ref(); + func(Some(&tmp)) + } + } + } #[stable(feature = "default_iters", since = "1.70.0")] impl Default for $name<'_, T> { /// Creates an empty slice iterator. diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index d2985d8a18669..4bb2368eec7a2 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -8,8 +8,8 @@ use super::{ }; use crate::fmt::{self, Write}; use crate::iter::{ - Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map, TrustedLen, TrustedRandomAccess, - TrustedRandomAccessNoCoerce, + Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map, PeekableIterator, TrustedLen, + TrustedRandomAccess, TrustedRandomAccessNoCoerce, }; use crate::num::NonZero; use crate::ops::Try; @@ -132,6 +132,14 @@ impl<'a> DoubleEndedIterator for Chars<'a> { } } +#[unstable(feature = "peekable_iterator", issue = "132973")] +impl<'a> PeekableIterator for Chars<'a> { + fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T { + let tmp = self.clone().next(); + func(tmp.as_ref()) + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Chars<'_> {}