Change:
In the stdlib I'd like an iterator like chunksexact that yields fixed-sized arrays, using a const usize argument (mostly useful for short arrays). .chunksexactct::<3() .map( a + b + c) Those arrays could allow better optimizations and also better pattern matching. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOT_END --
This also applies to the method on slices.
We should actually be able to implement this and test it out unstably now.
Im willing to give this a shot in the weekend and next week!
assign
So, a simple PoC fails to compile horribly <details<summary<strongBacktrace</strong</summary<p</p</details
Notice that a freestanding function does work:
The more I go into this, the more obstacles I stumble upon. Below a list of all ICE's I encountered:
Keep going on, those bugs/limitations are worth fixing anyway :)
to_array() could be a new function related to take(), it collects the first N items of an iterator into an array of length given as const generic argument (it panics if the iterator isn't long enough). Usable like this (using iterate() from itertools):
EDIT: This is already PR'd:
We need a good built-in static_assert that works with constant generics too :)

Discussion:
#![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(test))] #![feature(allocator_api)] <ins> #![feature(array_chunks)] </ins> #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] #![feature(box_patterns)]

use crate::boxed::Box; use crate::vec::Vec; <ins> #[unstable(feature = "array_chunks", issue = "74985")] pub use core::slice::ArrayChunks; </ins> #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[stable(feature = "from_ref", since = "1.28.0")]

#[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn windows(&self, size: usize) -> Windows<'_, T> { <del> assert!(size != 0); </del> <ins> assert_ne!(size, 0); </ins> Windows { v: self, size } }

#[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { <del> assert!(chunk_size != 0); </del> <ins> assert_ne!(chunk_size, 0); </ins> Chunks { v: self, chunk_size } }

#[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { <del> assert!(chunk_size != 0); </del> <ins> assert_ne!(chunk_size, 0); </ins> ChunksMut { v: self, chunk_size } }

#[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { <del> assert!(chunk_size != 0); </del> <ins> assert_ne!(chunk_size, 0); </ins> let rem = self.len() % chunk_size; let len = self.len() - rem; let (fst, snd) = self.split_at(len);

#[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { <del> assert!(chunk_size != 0); </del> <ins> assert_ne!(chunk_size, 0); </ins> 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 } } <ins> /// 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 } } </ins> /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end /// of the slice. ///

} } <ins> /// 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 } } </ins> /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a /// time), starting at the end of the slice. ///

#![feature(alloc_layout_extra)] <ins> #![feature(array_chunks)] </ins> #![feature(bool_to_option)] #![feature(bound_cloned)] #![feature(box_syntax)]

} #[test] <ins> 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] </ins> fn test_rchunks_count() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; let c = v.rchunks(3);