Skip to content

Commit b5eae9c

Browse files
committedAug 1, 2020
Auto merge of rust-lang#74373 - lcnr:array_chunks, r=withoutboats
add `slice::array_chunks` to std Now that rust-lang#74113 has landed, these methods are suddenly usable. A rebirth of rust-lang#72334 Tests are directly copied from `chunks_exact` and some additional tests for type inference. r? @withoutboats as you are both part of t-libs and working on const generics. closes rust-lang#60735
2 parents 18e2a89 + d51b71a commit b5eae9c

File tree

5 files changed

+243
-5
lines changed

5 files changed

+243
-5
lines changed
 

‎library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#![cfg_attr(not(test), feature(generator_trait))]
7676
#![cfg_attr(test, feature(test))]
7777
#![feature(allocator_api)]
78+
#![feature(array_chunks)]
7879
#![feature(allow_internal_unstable)]
7980
#![feature(arbitrary_self_types)]
8081
#![feature(box_patterns)]

‎library/alloc/src/slice.rs

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ use crate::borrow::ToOwned;
9595
use crate::boxed::Box;
9696
use crate::vec::Vec;
9797

98+
#[unstable(feature = "array_chunks", issue = "74985")]
99+
pub use core::slice::ArrayChunks;
98100
#[stable(feature = "slice_get_slice", since = "1.28.0")]
99101
pub use core::slice::SliceIndex;
100102
#[stable(feature = "from_ref", since = "1.28.0")]

‎library/core/src/slice/mod.rs

+148-5
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ impl<T> [T] {
680680
#[stable(feature = "rust1", since = "1.0.0")]
681681
#[inline]
682682
pub fn windows(&self, size: usize) -> Windows<'_, T> {
683-
assert!(size != 0);
683+
assert_ne!(size, 0);
684684
Windows { v: self, size }
685685
}
686686

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

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

@@ -789,7 +789,7 @@ impl<T> [T] {
789789
#[stable(feature = "chunks_exact", since = "1.31.0")]
790790
#[inline]
791791
pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> {
792-
assert!(chunk_size != 0);
792+
assert_ne!(chunk_size, 0);
793793
let rem = self.len() % chunk_size;
794794
let len = self.len() - rem;
795795
let (fst, snd) = self.split_at(len);
@@ -834,13 +834,52 @@ impl<T> [T] {
834834
#[stable(feature = "chunks_exact", since = "1.31.0")]
835835
#[inline]
836836
pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> {
837-
assert!(chunk_size != 0);
837+
assert_ne!(chunk_size, 0);
838838
let rem = self.len() % chunk_size;
839839
let len = self.len() - rem;
840840
let (fst, snd) = self.split_at_mut(len);
841841
ChunksExactMut { v: fst, rem: snd, chunk_size }
842842
}
843843

844+
/// Returns an iterator over `N` elements of the slice at a time, starting at the
845+
/// beginning of the slice.
846+
///
847+
/// The chunks are slices and do not overlap. If `N` does not divide the length of the
848+
/// slice, then the last up to `N-1` elements will be omitted and can be retrieved
849+
/// from the `remainder` function of the iterator.
850+
///
851+
/// This method is the const generic equivalent of [`chunks_exact`].
852+
///
853+
/// # Panics
854+
///
855+
/// Panics if `N` is 0. This check will most probably get changed to a compile time
856+
/// error before this method gets stabilized.
857+
///
858+
/// # Examples
859+
///
860+
/// ```
861+
/// #![feature(array_chunks)]
862+
/// let slice = ['l', 'o', 'r', 'e', 'm'];
863+
/// let mut iter = slice.array_chunks();
864+
/// assert_eq!(iter.next().unwrap(), &['l', 'o']);
865+
/// assert_eq!(iter.next().unwrap(), &['r', 'e']);
866+
/// assert!(iter.next().is_none());
867+
/// assert_eq!(iter.remainder(), &['m']);
868+
/// ```
869+
///
870+
/// [`chunks_exact`]: #method.chunks_exact
871+
#[unstable(feature = "array_chunks", issue = "74985")]
872+
#[inline]
873+
pub fn array_chunks<const N: usize>(&self) -> ArrayChunks<'_, T, N> {
874+
assert_ne!(N, 0);
875+
let len = self.len() / N;
876+
let (fst, snd) = self.split_at(len * N);
877+
// SAFETY: We cast a slice of `len * N` elements into
878+
// a slice of `len` many `N` elements chunks.
879+
let array_slice: &[[T; N]] = unsafe { from_raw_parts(fst.as_ptr().cast(), len) };
880+
ArrayChunks { iter: array_slice.iter(), rem: snd }
881+
}
882+
844883
/// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end
845884
/// of the slice.
846885
///
@@ -5432,6 +5471,110 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> {
54325471
}
54335472
}
54345473

5474+
/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a
5475+
/// time), starting at the beginning of the slice.
5476+
///
5477+
/// When the slice len is not evenly divided by the chunk size, the last
5478+
/// up to `chunk_size-1` elements will be omitted but can be retrieved from
5479+
/// the [`remainder`] function from the iterator.
5480+
///
5481+
/// This struct is created by the [`array_chunks`] method on [slices].
5482+
///
5483+
/// [`array_chunks`]: ../../std/primitive.slice.html#method.array_chunks
5484+
/// [`remainder`]: ../../std/slice/struct.ArrayChunks.html#method.remainder
5485+
/// [slices]: ../../std/primitive.slice.html
5486+
#[derive(Debug)]
5487+
#[unstable(feature = "array_chunks", issue = "74985")]
5488+
pub struct ArrayChunks<'a, T: 'a, const N: usize> {
5489+
iter: Iter<'a, [T; N]>,
5490+
rem: &'a [T],
5491+
}
5492+
5493+
impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
5494+
/// Returns the remainder of the original slice that is not going to be
5495+
/// returned by the iterator. The returned slice has at most `chunk_size-1`
5496+
/// elements.
5497+
#[unstable(feature = "array_chunks", issue = "74985")]
5498+
pub fn remainder(&self) -> &'a [T] {
5499+
self.rem
5500+
}
5501+
}
5502+
5503+
// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
5504+
#[unstable(feature = "array_chunks", issue = "74985")]
5505+
impl<T, const N: usize> Clone for ArrayChunks<'_, T, N> {
5506+
fn clone(&self) -> Self {
5507+
ArrayChunks { iter: self.iter.clone(), rem: self.rem }
5508+
}
5509+
}
5510+
5511+
#[unstable(feature = "array_chunks", issue = "74985")]
5512+
impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> {
5513+
type Item = &'a [T; N];
5514+
5515+
#[inline]
5516+
fn next(&mut self) -> Option<&'a [T; N]> {
5517+
self.iter.next()
5518+
}
5519+
5520+
#[inline]
5521+
fn size_hint(&self) -> (usize, Option<usize>) {
5522+
self.iter.size_hint()
5523+
}
5524+
5525+
#[inline]
5526+
fn count(self) -> usize {
5527+
self.iter.count()
5528+
}
5529+
5530+
#[inline]
5531+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
5532+
self.iter.nth(n)
5533+
}
5534+
5535+
#[inline]
5536+
fn last(self) -> Option<Self::Item> {
5537+
self.iter.last()
5538+
}
5539+
}
5540+
5541+
#[unstable(feature = "array_chunks", issue = "74985")]
5542+
impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> {
5543+
#[inline]
5544+
fn next_back(&mut self) -> Option<&'a [T; N]> {
5545+
self.iter.next_back()
5546+
}
5547+
5548+
#[inline]
5549+
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
5550+
self.iter.nth_back(n)
5551+
}
5552+
}
5553+
5554+
#[unstable(feature = "array_chunks", issue = "74985")]
5555+
impl<T, const N: usize> ExactSizeIterator for ArrayChunks<'_, T, N> {
5556+
fn is_empty(&self) -> bool {
5557+
self.iter.is_empty()
5558+
}
5559+
}
5560+
5561+
#[unstable(feature = "trusted_len", issue = "37572")]
5562+
unsafe impl<T, const N: usize> TrustedLen for ArrayChunks<'_, T, N> {}
5563+
5564+
#[unstable(feature = "array_chunks", issue = "74985")]
5565+
impl<T, const N: usize> FusedIterator for ArrayChunks<'_, T, N> {}
5566+
5567+
#[doc(hidden)]
5568+
#[unstable(feature = "array_chunks", issue = "74985")]
5569+
unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> {
5570+
unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T; N] {
5571+
unsafe { self.iter.get_unchecked(i) }
5572+
}
5573+
fn may_have_side_effect() -> bool {
5574+
false
5575+
}
5576+
}
5577+
54355578
/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
54365579
/// time), starting at the end of the slice.
54375580
///

‎library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![feature(alloc_layout_extra)]
2+
#![feature(array_chunks)]
23
#![feature(bool_to_option)]
34
#![feature(bound_cloned)]
45
#![feature(box_syntax)]

‎library/core/tests/slice.rs

+91
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,97 @@ fn test_chunks_exact_mut_zip() {
473473
assert_eq!(v1, [13, 14, 19, 20, 4]);
474474
}
475475

476+
#[test]
477+
fn test_array_chunks_infer() {
478+
let v: &[i32] = &[0, 1, 2, 3, 4, -4];
479+
let c = v.array_chunks();
480+
for &[a, b, c] in c {
481+
assert_eq!(a + b + c, 3);
482+
}
483+
484+
let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
485+
let total = v2.array_chunks().map(|&[a, b]| a * b).sum::<i32>();
486+
assert_eq!(total, 2 * 3 + 4 * 5);
487+
}
488+
489+
#[test]
490+
fn test_array_chunks_count() {
491+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
492+
let c = v.array_chunks::<3>();
493+
assert_eq!(c.count(), 2);
494+
495+
let v2: &[i32] = &[0, 1, 2, 3, 4];
496+
let c2 = v2.array_chunks::<2>();
497+
assert_eq!(c2.count(), 2);
498+
499+
let v3: &[i32] = &[];
500+
let c3 = v3.array_chunks::<2>();
501+
assert_eq!(c3.count(), 0);
502+
}
503+
504+
#[test]
505+
fn test_array_chunks_nth() {
506+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
507+
let mut c = v.array_chunks::<2>();
508+
assert_eq!(c.nth(1).unwrap(), &[2, 3]);
509+
assert_eq!(c.next().unwrap(), &[4, 5]);
510+
511+
let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6];
512+
let mut c2 = v2.array_chunks::<3>();
513+
assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
514+
assert_eq!(c2.next(), None);
515+
}
516+
517+
#[test]
518+
fn test_array_chunks_nth_back() {
519+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
520+
let mut c = v.array_chunks::<2>();
521+
assert_eq!(c.nth_back(1).unwrap(), &[2, 3]);
522+
assert_eq!(c.next().unwrap(), &[0, 1]);
523+
assert_eq!(c.next(), None);
524+
525+
let v2: &[i32] = &[0, 1, 2, 3, 4];
526+
let mut c2 = v2.array_chunks::<3>();
527+
assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]);
528+
assert_eq!(c2.next(), None);
529+
assert_eq!(c2.next_back(), None);
530+
531+
let v3: &[i32] = &[0, 1, 2, 3, 4];
532+
let mut c3 = v3.array_chunks::<10>();
533+
assert_eq!(c3.nth_back(0), None);
534+
}
535+
536+
#[test]
537+
fn test_array_chunks_last() {
538+
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
539+
let c = v.array_chunks::<2>();
540+
assert_eq!(c.last().unwrap(), &[4, 5]);
541+
542+
let v2: &[i32] = &[0, 1, 2, 3, 4];
543+
let c2 = v2.array_chunks::<2>();
544+
assert_eq!(c2.last().unwrap(), &[2, 3]);
545+
}
546+
547+
#[test]
548+
fn test_array_chunks_remainder() {
549+
let v: &[i32] = &[0, 1, 2, 3, 4];
550+
let c = v.array_chunks::<2>();
551+
assert_eq!(c.remainder(), &[4]);
552+
}
553+
554+
#[test]
555+
fn test_array_chunks_zip() {
556+
let v1: &[i32] = &[0, 1, 2, 3, 4];
557+
let v2: &[i32] = &[6, 7, 8, 9, 10];
558+
559+
let res = v1
560+
.array_chunks::<2>()
561+
.zip(v2.array_chunks::<2>())
562+
.map(|(a, b)| a.iter().sum::<i32>() + b.iter().sum::<i32>())
563+
.collect::<Vec<_>>();
564+
assert_eq!(res, vec![14, 22]);
565+
}
566+
476567
#[test]
477568
fn test_rchunks_count() {
478569
let v: &[i32] = &[0, 1, 2, 3, 4, 5];

0 commit comments

Comments
 (0)
Please sign in to comment.