Skip to content
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

add slice::array_chunks to std #74373

Merged
merged 7 commits into from
Aug 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#![cfg_attr(not(test), feature(generator_trait))]
#![cfg_attr(test, feature(test))]
#![feature(allocator_api)]
#![feature(array_chunks)]
#![feature(allow_internal_unstable)]
#![feature(arbitrary_self_types)]
#![feature(box_patterns)]
Expand Down
2 changes: 2 additions & 0 deletions library/alloc/src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ use crate::borrow::ToOwned;
use crate::boxed::Box;
use crate::vec::Vec;

#[unstable(feature = "array_chunks", issue = "74985")]
pub use core::slice::ArrayChunks;
#[stable(feature = "slice_get_slice", since = "1.28.0")]
pub use core::slice::SliceIndex;
#[stable(feature = "from_ref", since = "1.28.0")]
Expand Down
153 changes: 148 additions & 5 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn windows(&self, size: usize) -> Windows<'_, T> {
assert!(size != 0);
assert_ne!(size, 0);
Windows { v: self, size }
}

Expand Down Expand Up @@ -714,7 +714,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
Chunks { v: self, chunk_size }
}

Expand Down Expand Up @@ -752,7 +752,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
ChunksMut { v: self, chunk_size }
}

Expand Down Expand Up @@ -789,7 +789,7 @@ impl<T> [T] {
#[stable(feature = "chunks_exact", since = "1.31.0")]
#[inline]
pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
let rem = self.len() % chunk_size;
let len = self.len() - rem;
let (fst, snd) = self.split_at(len);
Expand Down Expand Up @@ -834,13 +834,52 @@ impl<T> [T] {
#[stable(feature = "chunks_exact", since = "1.31.0")]
#[inline]
pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> {
assert!(chunk_size != 0);
assert_ne!(chunk_size, 0);
let rem = self.len() % chunk_size;
let len = self.len() - rem;
let (fst, snd) = self.split_at_mut(len);
ChunksExactMut { v: fst, rem: snd, chunk_size }
}

/// Returns an iterator over `N` elements of the slice at a time, starting at the
/// beginning of the slice.
///
/// The chunks are slices and do not overlap. If `N` does not divide the length of the
/// slice, then the last up to `N-1` elements will be omitted and can be retrieved
/// from the `remainder` function of the iterator.
///
/// This method is the const generic equivalent of [`chunks_exact`].
///
/// # Panics
///
/// Panics if `N` is 0. This check will most probably get changed to a compile time
/// error before this method gets stabilized.
///
/// # Examples
///
/// ```
/// #![feature(array_chunks)]
/// let slice = ['l', 'o', 'r', 'e', 'm'];
/// let mut iter = slice.array_chunks();
/// assert_eq!(iter.next().unwrap(), &['l', 'o']);
/// assert_eq!(iter.next().unwrap(), &['r', 'e']);
/// assert!(iter.next().is_none());
/// assert_eq!(iter.remainder(), &['m']);
/// ```
///
/// [`chunks_exact`]: #method.chunks_exact
#[unstable(feature = "array_chunks", issue = "74985")]
#[inline]
pub fn array_chunks<const N: usize>(&self) -> ArrayChunks<'_, T, N> {
assert_ne!(N, 0);
let len = self.len() / N;
let (fst, snd) = self.split_at(len * N);
// SAFETY: We cast a slice of `len * N` elements into
// a slice of `len` many `N` elements chunks.
let array_slice: &[[T; N]] = unsafe { from_raw_parts(fst.as_ptr().cast(), len) };
ArrayChunks { iter: array_slice.iter(), rem: snd }
}

/// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end
/// of the slice.
///
Expand Down Expand Up @@ -5432,6 +5471,110 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> {
}
}

/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a
/// time), starting at the beginning of the slice.
///
/// When the slice len is not evenly divided by the chunk size, the last
/// up to `chunk_size-1` elements will be omitted but can be retrieved from
/// the [`remainder`] function from the iterator.
///
/// This struct is created by the [`array_chunks`] method on [slices].
///
/// [`array_chunks`]: ../../std/primitive.slice.html#method.array_chunks
/// [`remainder`]: ../../std/slice/struct.ArrayChunks.html#method.remainder
/// [slices]: ../../std/primitive.slice.html
#[derive(Debug)]
#[unstable(feature = "array_chunks", issue = "74985")]
pub struct ArrayChunks<'a, T: 'a, const N: usize> {
iter: Iter<'a, [T; N]>,
rem: &'a [T],
}

impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
/// Returns the remainder of the original slice that is not going to be
/// returned by the iterator. The returned slice has at most `chunk_size-1`
/// elements.
#[unstable(feature = "array_chunks", issue = "74985")]
pub fn remainder(&self) -> &'a [T] {
self.rem
}
}

// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
#[unstable(feature = "array_chunks", issue = "74985")]
impl<T, const N: usize> Clone for ArrayChunks<'_, T, N> {
fn clone(&self) -> Self {
ArrayChunks { iter: self.iter.clone(), rem: self.rem }
}
}

#[unstable(feature = "array_chunks", issue = "74985")]
impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> {
type Item = &'a [T; N];

#[inline]
fn next(&mut self) -> Option<&'a [T; N]> {
self.iter.next()
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn count(self) -> usize {
self.iter.count()
}

#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.iter.nth(n)
}

#[inline]
fn last(self) -> Option<Self::Item> {
self.iter.last()
}
}

#[unstable(feature = "array_chunks", issue = "74985")]
impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> {
#[inline]
fn next_back(&mut self) -> Option<&'a [T; N]> {
self.iter.next_back()
}

#[inline]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.iter.nth_back(n)
}
}

#[unstable(feature = "array_chunks", issue = "74985")]
impl<T, const N: usize> ExactSizeIterator for ArrayChunks<'_, T, N> {
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<T, const N: usize> TrustedLen for ArrayChunks<'_, T, N> {}

#[unstable(feature = "array_chunks", issue = "74985")]
impl<T, const N: usize> FusedIterator for ArrayChunks<'_, T, N> {}

#[doc(hidden)]
#[unstable(feature = "array_chunks", issue = "74985")]
unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> {
unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T; N] {
unsafe { self.iter.get_unchecked(i) }
}
fn may_have_side_effect() -> bool {
false
}
}

/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time), starting at the end of the slice.
///
Expand Down
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
#![feature(bool_to_option)]
#![feature(bound_cloned)]
#![feature(box_syntax)]
Expand Down
91 changes: 91 additions & 0 deletions library/core/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,97 @@ fn test_chunks_exact_mut_zip() {
assert_eq!(v1, [13, 14, 19, 20, 4]);
}

#[test]
fn test_array_chunks_infer() {
let v: &[i32] = &[0, 1, 2, 3, 4, -4];
let c = v.array_chunks();
for &[a, b, c] in c {
assert_eq!(a + b + c, 3);
}

let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
let total = v2.array_chunks().map(|&[a, b]| a * b).sum::<i32>();
assert_eq!(total, 2 * 3 + 4 * 5);
}

#[test]
fn test_array_chunks_count() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let c = v.array_chunks::<3>();
assert_eq!(c.count(), 2);

let v2: &[i32] = &[0, 1, 2, 3, 4];
let c2 = v2.array_chunks::<2>();
assert_eq!(c2.count(), 2);

let v3: &[i32] = &[];
let c3 = v3.array_chunks::<2>();
assert_eq!(c3.count(), 0);
}

#[test]
fn test_array_chunks_nth() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let mut c = v.array_chunks::<2>();
assert_eq!(c.nth(1).unwrap(), &[2, 3]);
assert_eq!(c.next().unwrap(), &[4, 5]);

let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
let mut c2 = v2.array_chunks::<3>();
assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
assert_eq!(c2.next(), None);
}

#[test]
fn test_array_chunks_nth_back() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let mut c = v.array_chunks::<2>();
assert_eq!(c.nth_back(1).unwrap(), &[2, 3]);
assert_eq!(c.next().unwrap(), &[0, 1]);
assert_eq!(c.next(), None);

let v2: &[i32] = &[0, 1, 2, 3, 4];
let mut c2 = v2.array_chunks::<3>();
assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]);
assert_eq!(c2.next(), None);
assert_eq!(c2.next_back(), None);

let v3: &[i32] = &[0, 1, 2, 3, 4];
let mut c3 = v3.array_chunks::<10>();
assert_eq!(c3.nth_back(0), None);
}

#[test]
fn test_array_chunks_last() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
let c = v.array_chunks::<2>();
assert_eq!(c.last().unwrap(), &[4, 5]);

let v2: &[i32] = &[0, 1, 2, 3, 4];
let c2 = v2.array_chunks::<2>();
assert_eq!(c2.last().unwrap(), &[2, 3]);
}

#[test]
fn test_array_chunks_remainder() {
let v: &[i32] = &[0, 1, 2, 3, 4];
let c = v.array_chunks::<2>();
assert_eq!(c.remainder(), &[4]);
}

#[test]
fn test_array_chunks_zip() {
let v1: &[i32] = &[0, 1, 2, 3, 4];
let v2: &[i32] = &[6, 7, 8, 9, 10];

let res = v1
.array_chunks::<2>()
.zip(v2.array_chunks::<2>())
.map(|(a, b)| a.iter().sum::<i32>() + b.iter().sum::<i32>())
.collect::<Vec<_>>();
assert_eq!(res, vec![14, 22]);
}

#[test]
fn test_rchunks_count() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
Expand Down