Skip to content

Add Iterator::array_chunks method #87776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 0 additions & 73 deletions library/core/src/iter/adapters/array_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,76 +71,3 @@ impl<I: Iterator, const N: usize> Iterator for ArrayChunks<I, N> {

#[unstable(feature = "iter_array_chunks", issue = "none")]
impl<I: FusedIterator, const N: usize> FusedIterator for ArrayChunks<I, N> {}

/// An iterator that yields the elements of another iterator in
/// chunks of size `N` starting from the end.
///
/// This `struct` is created by the [`array_rchunks`] method on [`Iterator`]. See
/// its documentation for more.
///
/// [`array_rchunks`]: Iterator::array_rchunks
#[unstable(feature = "iter_array_chunks", issue = "none")]
#[derive(Debug)]
pub struct ArrayRChunks<I: DoubleEndedIterator, const N: usize> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is array_rchunks actually redundant? Seems to me it's distinct from both .rev().array_chunks() (because the elements within the chunks are in the original order) and .array_chunks().rev() (when .remainder().len() != 0).

Copy link
Author

@johnschug johnschug Aug 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly redundant with the exception of the remainder its equivalent to:
.rev().array_chunks().map(|mut arr| { arr.reverse(); arr})

If it still seems useful, I can re-include it. Also ArrayChunks doesn't currently implement DoubleEndedIterator, although it could but it might produce surprising results if advanced from both directions. If it were implemented though, I think it could produce the same results as ArrayRChunks including the remainder.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have now added a DoubleEndedIterator implementation that seems to produce the same results array_rchunks would. It's not quite as efficient as it could be though, just to keep complexity down and share more code with the forward implementation.

iter: I,
buffer: [MaybeUninit<I::Item>; N],
init: usize,
}

#[unstable(feature = "iter_array_chunks", issue = "none")]
impl<I: DoubleEndedIterator, const N: usize> Drop for ArrayRChunks<I, N> {
fn drop(&mut self) {
// SAFETY: This is safe: `remainder_mut` returns exactly the sub-slice
// of elements that were initialized but not yielded and so have yet
// to be dropped.
unsafe {
ptr::drop_in_place(self.remainder_mut());
}
}
}

impl<I: DoubleEndedIterator, const N: usize> ArrayRChunks<I, N> {
pub(in crate::iter) fn new(iter: I) -> Self {
Self { iter, init: 0, buffer: MaybeUninit::uninit_array() }
}

/// Returns the remainder of the elements yielded by the original
/// iterator that were insufficient to fill another chunk. The
/// returned slice has at most `N-1` elements.
#[unstable(feature = "iter_array_chunks", issue = "none")]
pub fn remainder(&self) -> &[I::Item] {
// SAFETY: We know that all elements after `init` are properly initialized.
unsafe { MaybeUninit::slice_assume_init_ref(&self.buffer[(N - self.init)..]) }
}

/// Returns the remainder of the elements yielded by the original
/// iterator that were insufficient to fill another chunk. The
/// returned slice has at most `N-1` elements.
#[unstable(feature = "iter_array_chunks", issue = "none")]
pub fn remainder_mut(&mut self) -> &mut [I::Item] {
// SAFETY: We know that all elements after `init` are properly initialized.
unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buffer[(N - self.init)..]) }
}
}

#[unstable(feature = "iter_array_chunks", issue = "none")]
impl<I: DoubleEndedIterator, const N: usize> Iterator for ArrayRChunks<I, N> {
type Item = [I::Item; N];

fn next(&mut self) -> Option<Self::Item> {
while self.init < N {
self.buffer[N - self.init - 1] = MaybeUninit::new(self.iter.next_back()?);
self.init += 1;
}
self.init = 0;
// SAFETY: This is safe: `MaybeUninit<T>` is guaranteed to have the same layout
// as `T` and the entire array has just been initialized.
unsafe { Some(mem::transmute_copy(&self.buffer)) }
}
}

#[unstable(feature = "iter_array_chunks", issue = "none")]
impl<I, const N: usize> FusedIterator for ArrayRChunks<I, N> where
I: DoubleEndedIterator + FusedIterator
{
}
2 changes: 1 addition & 1 deletion library/core/src/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use self::{
};

#[unstable(feature = "iter_array_chunks", issue = "none")]
pub use self::array_chunks::{ArrayChunks, ArrayRChunks};
pub use self::array_chunks::ArrayChunks;

#[stable(feature = "iter_cloned", since = "1.1.0")]
pub use self::cloned::Cloned;
Expand Down
4 changes: 2 additions & 2 deletions library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ pub use self::traits::{

#[unstable(feature = "iter_zip", issue = "83574")]
pub use self::adapters::zip;
#[unstable(feature = "iter_array_chunks", issue = "none")]
pub use self::adapters::ArrayChunks;
#[stable(feature = "iter_cloned", since = "1.1.0")]
pub use self::adapters::Cloned;
#[stable(feature = "iter_copied", since = "1.36.0")]
Expand All @@ -409,8 +411,6 @@ pub use self::adapters::StepBy;
pub use self::adapters::TrustedRandomAccess;
#[unstable(feature = "trusted_random_access", issue = "none")]
pub use self::adapters::TrustedRandomAccessNoCoerce;
#[unstable(feature = "iter_array_chunks", issue = "none")]
pub use self::adapters::{ArrayChunks, ArrayRChunks};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::adapters::{
Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan,
Expand Down
32 changes: 1 addition & 31 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use crate::cmp::{self, Ordering};
use crate::ops::{ControlFlow, Try};

use super::super::ArrayChunks;
use super::super::TrustedRandomAccessNoCoerce;
use super::super::{ArrayChunks, ArrayRChunks};
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};
Expand Down Expand Up @@ -3499,36 +3499,6 @@ pub trait Iterator {
assert_ne!(N, 0);
ArrayChunks::new(self)
}

/// Creates an iterator which yields arrays of `N` elements yielded by
/// the original iterator starting from the end.
///
/// # Panics
///
/// Panics if `N` is zero.
///
/// # Examples
///
/// ```
/// #![feature(iter_array_chunks)]
/// let mut iter = (0..10).array_rchunks::<3>();
///
/// assert_eq!(iter.next(), Some([7, 8, 9]));
/// assert_eq!(iter.next(), Some([4, 5, 6]));
/// assert_eq!(iter.next(), Some([1, 2, 3]));
/// assert_eq!(iter.next(), None);
///
/// assert_eq!(iter.remainder(), &[0]);
/// ```
#[inline]
#[unstable(feature = "iter_array_chunks", issue = "none")]
fn array_rchunks<const N: usize>(self) -> ArrayRChunks<Self, N>
where
Self: Sized + DoubleEndedIterator,
{
assert_ne!(N, 0);
ArrayRChunks::new(self)
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
48 changes: 0 additions & 48 deletions library/core/tests/iter/adapters/array_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,51 +64,3 @@ fn test_iterator_array_chunks_drop() {
drop(iter);
assert_eq!(counter.get(), 5);
}

#[test]
fn test_iterator_array_rchunks_remainder() {
let mut iter = (0..=10).array_rchunks::<4>();
assert_eq!(iter.remainder(), &[]);
assert_eq!(iter.remainder_mut(), &[]);
assert_eq!(iter.next(), Some([7, 8, 9, 10]));
assert_eq!(iter.remainder(), &[]);
assert_eq!(iter.remainder_mut(), &[]);
assert_eq!(iter.next(), Some([3, 4, 5, 6]));
assert_eq!(iter.remainder(), &[]);
assert_eq!(iter.remainder_mut(), &[]);
assert_eq!(iter.next(), None);
assert_eq!(iter.remainder(), &[0, 1, 2]);
assert_eq!(iter.remainder_mut(), &[0, 1, 2]);
}

#[test]
fn test_iterator_array_rchunks_drop() {
let counter = Cell::new(0);
let create =
|n| (0..n).map(|_| DropBomb { dropped: false, counter: &counter }).array_rchunks::<3>();

let iter = create(5);
assert_eq!(counter.get(), 0);
drop(iter);
assert_eq!(counter.get(), 0);

let mut iter = create(3);
counter.set(0);
iter.next();
assert_eq!(counter.get(), 3);
assert!(iter.next().is_none());
assert_eq!(counter.get(), 3);
assert_eq!(iter.remainder().len(), 0);
drop(iter);
assert_eq!(counter.get(), 3);

let mut iter = create(5);
counter.set(0);
iter.next();
assert_eq!(counter.get(), 3);
assert!(iter.next().is_none());
assert_eq!(counter.get(), 3);
assert_eq!(iter.remainder().len(), 2);
drop(iter);
assert_eq!(counter.get(), 5);
}