diff --git a/library/core/src/iter/adapters/array_windows.rs b/library/core/src/iter/adapters/array_windows.rs new file mode 100644 index 0000000000000..cb361f999d294 --- /dev/null +++ b/library/core/src/iter/adapters/array_windows.rs @@ -0,0 +1,102 @@ +use crate::iter::{ExactSizeIterator, Fuse, FusedIterator, Iterator, TrustedLen}; + +use crate::array; + +/// An iterator over all contiguous windows of length `N`. The windows overlap. +/// If the iterator is shorter than `N`, the iterator returns no values. +/// +/// This `struct` is created by [`Iterator::array_windows`]. See its +/// documentation for more. +#[derive(Debug, Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] +pub struct ArrayWindows +where + I: Iterator, + I::Item: Clone, +{ + iter: Fuse, + last: Option<[I::Item; N]>, +} + +impl ArrayWindows +where + I: Iterator, + I::Item: Clone, +{ + #[inline] + pub(in crate::iter) fn new(iter: I) -> Self { + assert!(N != 0); + Self { iter: iter.fuse(), last: None } + } +} + +#[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] +impl Iterator for ArrayWindows +where + I: Iterator, + I::Item: Clone, +{ + type Item = [I::Item; N]; + + #[inline] + fn next(&mut self) -> Option { + let Self { iter, last } = self; + + match last { + Some(last) => { + let item = iter.next()?; + last.rotate_left(1); + if let Some(end) = last.last_mut() { + *end = item; + } + Some(last.clone()) + } + None => { + let tmp = array::try_from_fn(|_| iter.next())?; + *last = Some(tmp.clone()); + Some(tmp) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (lower, upper) = self.iter.size_hint(); + // Keep infinite iterator size hint lower bound as `usize::MAX` + if lower == usize::MAX { + (lower, upper) + } else { + (lower.saturating_sub(N - 1), upper.map(|n| n.saturating_sub(N - 1))) + } + } + + #[inline] + fn count(self) -> usize { + self.iter.count().saturating_sub(N - 1) + } +} + +#[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] +impl FusedIterator for ArrayWindows +where + I: FusedIterator, + I::Item: Clone, +{ +} + +#[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] +impl ExactSizeIterator for ArrayWindows +where + I: ExactSizeIterator, + I::Item: Clone, +{ +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayWindows +where + I: TrustedLen, + I::Item: Clone, +{ +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index b1b917775c3fa..bfc0828e6548a 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -1,6 +1,7 @@ use crate::iter::{InPlaceIterable, Iterator}; use crate::ops::{ControlFlow, Try}; +mod array_windows; mod chain; mod cloned; mod copied; @@ -30,6 +31,9 @@ pub use self::{ scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, }; +#[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] +pub use self::array_windows::ArrayWindows; + #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index da459ed7c68f4..d16d7fea0adf9 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -393,6 +393,8 @@ pub use self::traits::{ #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::adapters::zip; +#[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] +pub use self::adapters::ArrayWindows; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::adapters::Cloned; #[stable(feature = "iter_copied", since = "1.36.0")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 9a9a844f41bb4..5a4b3039f87dc 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2,12 +2,13 @@ use crate::cmp::{self, Ordering}; use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; use super::super::TrustedRandomAccessNoCoerce; +use super::super::{ + ArrayWindows, Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, + TakeWhile, +}; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; -use super::super::{ - Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, -}; fn _assert_is_object_safe(_: &dyn Iterator) {} @@ -3057,6 +3058,49 @@ pub trait Iterator { Cycle::new(self) } + /// Returns an iterator over all contiguous windows of length `N`. The + /// windows overlap. If the iterator is shorter than `N`, the iterator + /// returns no values. + /// + /// `array_windows` clones the iterator elements so that they can be part of + /// successive windows, this makes this it most suited for iterators of + /// references and other values that are cheap to clone. + /// + /// # Panics + /// + /// If called with `N = 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_array_windows)] + /// + /// let mut iter = "rust".chars().array_windows(); + /// assert_eq!(iter.next(), Some(['r', 'u'])); + /// assert_eq!(iter.next(), Some(['u', 's'])); + /// assert_eq!(iter.next(), Some(['s', 't'])); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// ``` + /// #![feature(iter_array_windows)] + /// + /// let seq: &[i32] = &[0, 1, 1, 2, 3, 5, 8, 13]; + /// for [x, y, z] in seq.iter().copied().array_windows() { + /// assert_eq!(x + y, z); + /// } + /// ``` + #[unstable(feature = "iter_array_windows", reason = "recently added", issue = "none")] + fn array_windows(self) -> ArrayWindows + where + Self: Sized, + Self::Item: Clone, + { + ArrayWindows::new(self) + } + /// Sums the elements of an iterator. /// /// Takes each element, adds them together, and returns the result. diff --git a/library/core/tests/iter/adapters/array_windows.rs b/library/core/tests/iter/adapters/array_windows.rs new file mode 100644 index 0000000000000..dac2ace17a71e --- /dev/null +++ b/library/core/tests/iter/adapters/array_windows.rs @@ -0,0 +1,65 @@ +use core::iter; + +#[test] +fn test_array_windows_infer() { + let s = [0, 1, 0, 1, 0, 1]; + for [a, b] in s.iter().copied().array_windows() { + assert_eq!(a + b, 1); + } + for [a, b, c, d] in s.iter().copied().array_windows() { + assert_eq!(a + b + c + d, 2); + } +} + +#[test] +fn test_array_windows_size_hint() { + let iter = (0..6).array_windows::<1>(); + assert_eq!(iter.size_hint(), (6, Some(6))); + + let iter = (0..6).array_windows::<3>(); + assert_eq!(iter.size_hint(), (4, Some(4))); + + let iter = (0..6).array_windows::<5>(); + assert_eq!(iter.size_hint(), (2, Some(2))); + + let iter = (0..6).array_windows::<7>(); + assert_eq!(iter.size_hint(), (0, Some(0))); + + let iter = (1..).array_windows::<2>(); + assert_eq!(iter.size_hint(), (usize::MAX, None)); + + let iter = (1..).filter(|x| x % 2 != 0).array_windows::<2>(); + assert_eq!(iter.size_hint(), (0, None)); +} + +#[test] +fn test_array_windows_count() { + let iter = (0..6).array_windows::<1>(); + assert_eq!(iter.count(), 6); + + let iter = (0..6).array_windows::<3>(); + assert_eq!(iter.count(), 4); + + let iter = (0..6).array_windows::<5>(); + assert_eq!(iter.count(), 2); + + let iter = (0..6).array_windows::<7>(); + assert_eq!(iter.count(), 0); + + let iter = (0..6).filter(|x| x % 2 == 0).array_windows::<2>(); + assert_eq!(iter.count(), 2); + + let iter = iter::empty::().array_windows::<2>(); + assert_eq!(iter.count(), 0); + + let iter = [(); usize::MAX].iter().array_windows::<2>(); + assert_eq!(iter.count(), usize::MAX - 1); +} + +#[test] +fn test_array_windows_nth() { + let mut iter = (0..6).array_windows::<4>(); + assert_eq!(iter.nth(1), Some([1, 2, 3, 4])); + assert_eq!(iter.nth(0), Some([2, 3, 4, 5])); + assert_eq!(iter.nth(1), None); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index 567d9fe49cade..0db1a352e9915 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -1,3 +1,4 @@ +mod array_windows; mod chain; mod cloned; mod copied; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index ec700346ac91d..9b5c2c8e4e245 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -60,6 +60,7 @@ #![feature(slice_partition_dedup)] #![feature(int_log)] #![feature(iter_advance_by)] +#![feature(iter_array_windows)] #![feature(iter_partition_in_place)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)]