mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 15:01:51 +00:00
Add Iterator::array_chunks()
This commit is contained in:
parent
1f5d8d49eb
commit
ca3d1010bb
427
library/core/src/iter/adapters/array_chunks.rs
Normal file
427
library/core/src/iter/adapters/array_chunks.rs
Normal file
@ -0,0 +1,427 @@
|
||||
use crate::iter::{Fuse, FusedIterator, Iterator, TrustedLen};
|
||||
use crate::mem;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ops::{ControlFlow, Try};
|
||||
use crate::ptr;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Remainder<T, const N: usize> {
|
||||
array: [MaybeUninit<T>; N],
|
||||
init: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Remainder<T, N> {
|
||||
fn new() -> Self {
|
||||
Self { array: MaybeUninit::uninit_array(), init: 0 }
|
||||
}
|
||||
|
||||
unsafe fn with_init(array: [MaybeUninit<T>; N], init: usize) -> Self {
|
||||
Self { array, init }
|
||||
}
|
||||
|
||||
fn as_slice(&self) -> &[T] {
|
||||
debug_assert!(self.init <= N);
|
||||
// SAFETY: This raw slice will only contain the initialized objects
|
||||
// within the buffer.
|
||||
unsafe {
|
||||
let slice = self.array.get_unchecked(..self.init);
|
||||
MaybeUninit::slice_assume_init_ref(slice)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
debug_assert!(self.init <= N);
|
||||
// SAFETY: This raw slice will only contain the initialized objects
|
||||
// within the buffer.
|
||||
unsafe {
|
||||
let slice = self.array.get_unchecked_mut(..self.init);
|
||||
MaybeUninit::slice_assume_init_mut(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Clone for Remainder<T, N>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
let mut new = Self::new();
|
||||
// SAFETY: The new array is the same size and `init` is always less than
|
||||
// or equal to `N`.
|
||||
let this = unsafe { new.array.get_unchecked_mut(..self.init) };
|
||||
MaybeUninit::write_slice_cloned(this, self.as_slice());
|
||||
new.init = self.init;
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for Remainder<T, N> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: This raw slice will only contain the initialized objects
|
||||
// within the buffer.
|
||||
unsafe { ptr::drop_in_place(self.as_mut_slice()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over `N` elements of the iterator at a time.
|
||||
///
|
||||
/// The chunks do not overlap. If `N` does not divide the length of the
|
||||
/// iterator, then the last up to `N-1` elements will be omitted.
|
||||
///
|
||||
/// This `struct` is created by the [`array_chunks`][Iterator::array_chunks]
|
||||
/// method on [`Iterator`]. See its documentation for more.
|
||||
#[derive(Debug, Clone)]
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
pub struct ArrayChunks<I: Iterator, const N: usize> {
|
||||
iter: Fuse<I>,
|
||||
remainder: Remainder<I::Item, N>,
|
||||
}
|
||||
|
||||
impl<I, const N: usize> ArrayChunks<I, N>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
pub(in crate::iter) fn new(iter: I) -> Self {
|
||||
assert!(N != 0, "chunk size must be non-zero");
|
||||
Self { iter: iter.fuse(), remainder: Remainder::new() }
|
||||
}
|
||||
|
||||
/// Returns a reference to the remaining elements of the original iterator
|
||||
/// that are not going to be returned by this iterator. The returned slice
|
||||
/// has at most `N-1` elements.
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
#[inline]
|
||||
pub fn remainder(&self) -> &[I::Item] {
|
||||
self.remainder.as_slice()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the remaining elements of the original
|
||||
/// iterator that are not going to be returned by this iterator. The
|
||||
/// returned slice has at most `N-1` elements.
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
#[inline]
|
||||
pub fn remainder_mut(&mut self) -> &mut [I::Item] {
|
||||
self.remainder.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
impl<I, const N: usize> Iterator for ArrayChunks<I, N>
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
type Item = [I::Item; N];
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
// SAFETY: `array` will still be valid if `guard` is dropped.
|
||||
let mut guard = unsafe { FrontGuard::new(&mut array) };
|
||||
|
||||
for slot in array.iter_mut() {
|
||||
match self.iter.next() {
|
||||
Some(item) => {
|
||||
slot.write(item);
|
||||
guard.init += 1;
|
||||
}
|
||||
None => {
|
||||
if guard.init > 0 {
|
||||
let init = guard.init;
|
||||
mem::forget(guard);
|
||||
// SAFETY: `array` was initialized with `init` elements.
|
||||
self.remainder = unsafe { Remainder::with_init(array, init) };
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mem::forget(guard);
|
||||
// SAFETY: All elements of the array were populated in the loop above.
|
||||
Some(unsafe { MaybeUninit::array_assume_init(array) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (lower, upper) = self.iter.size_hint();
|
||||
// Keep infinite iterator size hint lower bound as `usize::MAX`. This
|
||||
// is required to implement `TrustedLen`.
|
||||
if lower == usize::MAX {
|
||||
return (lower, upper);
|
||||
}
|
||||
(lower / N, upper.map(|n| n / N))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count(self) -> usize {
|
||||
self.iter.count() / N
|
||||
}
|
||||
|
||||
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> R,
|
||||
R: Try<Output = B>,
|
||||
{
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
// SAFETY: `array` will still be valid if `guard` is dropped.
|
||||
let mut guard = unsafe { FrontGuard::new(&mut array) };
|
||||
|
||||
let result = self.iter.try_fold(init, |mut acc, item| {
|
||||
// SAFETY: `init` starts at 0, increases by one each iteration and
|
||||
// is reset to 0 once it reaches N.
|
||||
unsafe { array.get_unchecked_mut(guard.init) }.write(item);
|
||||
guard.init += 1;
|
||||
if guard.init == N {
|
||||
guard.init = 0;
|
||||
let array = mem::replace(&mut array, MaybeUninit::uninit_array());
|
||||
// SAFETY: the condition above asserts that all elements are
|
||||
// initialized.
|
||||
let item = unsafe { MaybeUninit::array_assume_init(array) };
|
||||
acc = f(acc, item)?;
|
||||
}
|
||||
R::from_output(acc)
|
||||
});
|
||||
match result.branch() {
|
||||
ControlFlow::Continue(o) => {
|
||||
if guard.init > 0 {
|
||||
let init = guard.init;
|
||||
mem::forget(guard);
|
||||
// SAFETY: `array` was initialized with `init` elements.
|
||||
self.remainder = unsafe { Remainder::with_init(array, init) };
|
||||
}
|
||||
R::from_output(o)
|
||||
}
|
||||
ControlFlow::Break(r) => R::from_residual(r),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold<B, F>(self, init: B, mut f: F) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
// SAFETY: `array` will still be valid if `guard` is dropped.
|
||||
let mut guard = unsafe { FrontGuard::new(&mut array) };
|
||||
|
||||
self.iter.fold(init, |mut acc, item| {
|
||||
// SAFETY: `init` starts at 0, increases by one each iteration and
|
||||
// is reset to 0 once it reaches N.
|
||||
unsafe { array.get_unchecked_mut(guard.init) }.write(item);
|
||||
guard.init += 1;
|
||||
if guard.init == N {
|
||||
guard.init = 0;
|
||||
let array = mem::replace(&mut array, MaybeUninit::uninit_array());
|
||||
// SAFETY: the condition above asserts that all elements are
|
||||
// initialized.
|
||||
let item = unsafe { MaybeUninit::array_assume_init(array) };
|
||||
acc = f(acc, item);
|
||||
}
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard for an array where elements are filled from the left.
|
||||
struct FrontGuard<T, const N: usize> {
|
||||
/// A pointer to the array that is being filled. We need to use a raw
|
||||
/// pointer here because of the lifetime issues in the fold implementations.
|
||||
ptr: *mut T,
|
||||
/// The number of *initialized* elements.
|
||||
init: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> FrontGuard<T, N> {
|
||||
unsafe fn new(array: &mut [MaybeUninit<T>; N]) -> Self {
|
||||
Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for FrontGuard<T, N> {
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.init <= N);
|
||||
// SAFETY: This raw slice will only contain the initialized objects
|
||||
// within the buffer.
|
||||
unsafe {
|
||||
let slice = ptr::slice_from_raw_parts_mut(self.ptr, self.init);
|
||||
ptr::drop_in_place(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
impl<I, const N: usize> DoubleEndedIterator for ArrayChunks<I, N>
|
||||
where
|
||||
I: DoubleEndedIterator + ExactSizeIterator,
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
// We are iterating from the back we need to first handle the remainder.
|
||||
self.next_back_remainder()?;
|
||||
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
// SAFETY: `array` will still be valid if `guard` is dropped.
|
||||
let mut guard = unsafe { BackGuard::new(&mut array) };
|
||||
|
||||
for slot in array.iter_mut().rev() {
|
||||
slot.write(self.iter.next_back()?);
|
||||
guard.uninit -= 1;
|
||||
}
|
||||
|
||||
mem::forget(guard);
|
||||
// SAFETY: All elements of the array were populated in the loop above.
|
||||
Some(unsafe { MaybeUninit::array_assume_init(array) })
|
||||
}
|
||||
|
||||
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> R,
|
||||
R: Try<Output = B>,
|
||||
{
|
||||
// We are iterating from the back we need to first handle the remainder.
|
||||
if self.next_back_remainder().is_none() {
|
||||
return R::from_output(init);
|
||||
}
|
||||
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
// SAFETY: `array` will still be valid if `guard` is dropped.
|
||||
let mut guard = unsafe { BackGuard::new(&mut array) };
|
||||
|
||||
self.iter.try_rfold(init, |mut acc, item| {
|
||||
guard.uninit -= 1;
|
||||
// SAFETY: `uninit` starts at N, decreases by one each iteration and
|
||||
// is reset to N once it reaches 0.
|
||||
unsafe { array.get_unchecked_mut(guard.uninit) }.write(item);
|
||||
if guard.uninit == 0 {
|
||||
guard.uninit = N;
|
||||
let array = mem::replace(&mut array, MaybeUninit::uninit_array());
|
||||
// SAFETY: the condition above asserts that all elements are
|
||||
// initialized.
|
||||
let item = unsafe { MaybeUninit::array_assume_init(array) };
|
||||
acc = f(acc, item)?;
|
||||
}
|
||||
R::from_output(acc)
|
||||
})
|
||||
}
|
||||
|
||||
fn rfold<B, F>(mut self, init: B, mut f: F) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
// We are iterating from the back we need to first handle the remainder.
|
||||
if self.next_back_remainder().is_none() {
|
||||
return init;
|
||||
}
|
||||
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
|
||||
// SAFETY: `array` will still be valid if `guard` is dropped.
|
||||
let mut guard = unsafe { BackGuard::new(&mut array) };
|
||||
|
||||
self.iter.rfold(init, |mut acc, item| {
|
||||
guard.uninit -= 1;
|
||||
// SAFETY: `uninit` starts at N, decreases by one each iteration and
|
||||
// is reset to N once it reaches 0.
|
||||
unsafe { array.get_unchecked_mut(guard.uninit) }.write(item);
|
||||
if guard.uninit == 0 {
|
||||
guard.uninit = N;
|
||||
let array = mem::replace(&mut array, MaybeUninit::uninit_array());
|
||||
// SAFETY: the condition above asserts that all elements are
|
||||
// initialized.
|
||||
let item = unsafe { MaybeUninit::array_assume_init(array) };
|
||||
acc = f(acc, item);
|
||||
}
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, const N: usize> ArrayChunks<I, N>
|
||||
where
|
||||
I: DoubleEndedIterator + ExactSizeIterator,
|
||||
{
|
||||
#[inline]
|
||||
fn next_back_remainder(&mut self) -> Option<()> {
|
||||
// We use the `ExactSizeIterator` implementation of the underlying
|
||||
// iterator to know how many remaining elements there are.
|
||||
let rem = self.iter.len() % N;
|
||||
if rem == 0 {
|
||||
return Some(());
|
||||
}
|
||||
|
||||
let mut array = MaybeUninit::uninit_array();
|
||||
|
||||
// SAFETY: The array will still be valid if `guard` is dropped and
|
||||
// it is forgotten otherwise.
|
||||
let mut guard = unsafe { FrontGuard::new(&mut array) };
|
||||
|
||||
// SAFETY: `rem` is in the range 1..N based on how it is calculated.
|
||||
for slot in unsafe { array.get_unchecked_mut(..rem) }.iter_mut() {
|
||||
slot.write(self.iter.next_back()?);
|
||||
guard.init += 1;
|
||||
}
|
||||
|
||||
let init = guard.init;
|
||||
mem::forget(guard);
|
||||
// SAFETY: `array` was initialized with exactly `init` elements.
|
||||
self.remainder = unsafe {
|
||||
array.get_unchecked_mut(..init).reverse();
|
||||
Remainder::with_init(array, init)
|
||||
};
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard for an array where elements are filled from the right.
|
||||
struct BackGuard<T, const N: usize> {
|
||||
/// A pointer to the array that is being filled. We need to use a raw
|
||||
/// pointer here because of the lifetime issues in the rfold implementations.
|
||||
ptr: *mut T,
|
||||
/// The number of *uninitialized* elements.
|
||||
uninit: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> BackGuard<T, N> {
|
||||
unsafe fn new(array: &mut [MaybeUninit<T>; N]) -> Self {
|
||||
Self { ptr: MaybeUninit::slice_as_mut_ptr(array), uninit: N }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for BackGuard<T, N> {
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.uninit <= N);
|
||||
// SAFETY: This raw slice will only contain the initialized objects
|
||||
// within the buffer.
|
||||
unsafe {
|
||||
let ptr = self.ptr.offset(self.uninit as isize);
|
||||
let slice = ptr::slice_from_raw_parts_mut(ptr, N - self.uninit);
|
||||
ptr::drop_in_place(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
impl<I, const N: usize> FusedIterator for ArrayChunks<I, N> where I: FusedIterator {}
|
||||
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
impl<I, const N: usize> ExactSizeIterator for ArrayChunks<I, N>
|
||||
where
|
||||
I: ExactSizeIterator,
|
||||
{
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len() / N
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.iter.len() / N == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
unsafe impl<I, const N: usize> TrustedLen for ArrayChunks<I, N> where I: TrustedLen {}
|
@ -1,6 +1,7 @@
|
||||
use crate::iter::{InPlaceIterable, Iterator};
|
||||
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, NeverShortCircuit, Residual, Try};
|
||||
|
||||
mod array_chunks;
|
||||
mod by_ref_sized;
|
||||
mod chain;
|
||||
mod cloned;
|
||||
@ -32,6 +33,9 @@ pub use self::{
|
||||
scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip,
|
||||
};
|
||||
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
pub use self::array_chunks::ArrayChunks;
|
||||
|
||||
#[unstable(feature = "std_internals", issue = "none")]
|
||||
pub use self::by_ref_sized::ByRefSized;
|
||||
|
||||
|
@ -398,6 +398,8 @@ pub use self::traits::{
|
||||
|
||||
#[stable(feature = "iter_zip", since = "1.59.0")]
|
||||
pub use self::adapters::zip;
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
pub use self::adapters::ArrayChunks;
|
||||
#[unstable(feature = "std_internals", issue = "none")]
|
||||
pub use self::adapters::ByRefSized;
|
||||
#[stable(feature = "iter_cloned", since = "1.1.0")]
|
||||
|
@ -5,7 +5,7 @@ use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
|
||||
use super::super::try_process;
|
||||
use super::super::ByRefSized;
|
||||
use super::super::TrustedRandomAccessNoCoerce;
|
||||
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
|
||||
use super::super::{ArrayChunks, 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::{
|
||||
@ -3316,6 +3316,46 @@ pub trait Iterator {
|
||||
Cycle::new(self)
|
||||
}
|
||||
|
||||
/// Returns an iterator over `N` elements of the iterator at a time.
|
||||
///
|
||||
/// The chunks do not overlap. If `N` does not divide the length of the
|
||||
/// iterator, then the last up to `N-1` elements will be omitted.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `N` is 0.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(iter_array_chunks)]
|
||||
///
|
||||
/// let mut iter = "lorem".chars().array_chunks();
|
||||
/// assert_eq!(iter.next(), Some(['l', 'o']));
|
||||
/// assert_eq!(iter.next(), Some(['r', 'e']));
|
||||
/// assert_eq!(iter.next(), None);
|
||||
/// assert_eq!(iter.remainder(), &['m']);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(iter_array_chunks)]
|
||||
///
|
||||
/// let data = [1, 1, 2, -2, 6, 0, 3, 1];
|
||||
/// // ^-----^ ^------^
|
||||
/// for [x, y, z] in data.iter().array_chunks() {
|
||||
/// assert_eq!(x + y + z, 4);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
|
||||
fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ArrayChunks::new(self)
|
||||
}
|
||||
|
||||
/// Sums the elements of an iterator.
|
||||
///
|
||||
/// Takes each element, adds them together, and returns the result.
|
||||
|
198
library/core/tests/iter/adapters/array_chunks.rs
Normal file
198
library/core/tests/iter/adapters/array_chunks.rs
Normal file
@ -0,0 +1,198 @@
|
||||
use core::cell::Cell;
|
||||
use core::iter::{self, Iterator};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_infer() {
|
||||
let xs = [1, 1, 2, -2, 6, 0, 3, 1];
|
||||
for [a, b, c] in xs.iter().copied().array_chunks() {
|
||||
assert_eq!(a + b + c, 4);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_clone_and_drop() {
|
||||
let count = Cell::new(0);
|
||||
let mut it = (0..5).map(|_| CountDrop::new(&count)).array_chunks::<3>();
|
||||
|
||||
assert_eq!(it.by_ref().count(), 1);
|
||||
assert_eq!(count.get(), 3);
|
||||
assert_eq!(it.remainder().len(), 2);
|
||||
|
||||
let mut it2 = it.clone();
|
||||
assert_eq!(count.get(), 3);
|
||||
assert_eq!(it2.remainder().len(), 2);
|
||||
|
||||
drop(it);
|
||||
assert_eq!(count.get(), 5);
|
||||
assert_eq!(it2.remainder().len(), 2);
|
||||
assert!(it2.next().is_none());
|
||||
|
||||
drop(it2);
|
||||
assert_eq!(count.get(), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_remainder() {
|
||||
let mut it = (0..11).array_chunks::<4>();
|
||||
assert_eq!(it.remainder(), &[]);
|
||||
assert_eq!(it.remainder_mut(), &[]);
|
||||
assert_eq!(it.next(), Some([0, 1, 2, 3]));
|
||||
assert_eq!(it.remainder(), &[]);
|
||||
assert_eq!(it.remainder_mut(), &[]);
|
||||
assert_eq!(it.next(), Some([4, 5, 6, 7]));
|
||||
assert_eq!(it.remainder(), &[]);
|
||||
assert_eq!(it.remainder_mut(), &[]);
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.remainder(), &[8, 9, 10]);
|
||||
assert_eq!(it.remainder_mut(), &[8, 9, 10]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_size_hint() {
|
||||
let it = (0..6).array_chunks::<1>();
|
||||
assert_eq!(it.size_hint(), (6, Some(6)));
|
||||
|
||||
let it = (0..6).array_chunks::<3>();
|
||||
assert_eq!(it.size_hint(), (2, Some(2)));
|
||||
|
||||
let it = (0..6).array_chunks::<5>();
|
||||
assert_eq!(it.size_hint(), (1, Some(1)));
|
||||
|
||||
let it = (0..6).array_chunks::<7>();
|
||||
assert_eq!(it.size_hint(), (0, Some(0)));
|
||||
|
||||
let it = (1..).array_chunks::<2>();
|
||||
assert_eq!(it.size_hint(), (usize::MAX, None));
|
||||
|
||||
let it = (1..).filter(|x| x % 2 != 0).array_chunks::<2>();
|
||||
assert_eq!(it.size_hint(), (0, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_count() {
|
||||
let it = (0..6).array_chunks::<1>();
|
||||
assert_eq!(it.count(), 6);
|
||||
|
||||
let it = (0..6).array_chunks::<3>();
|
||||
assert_eq!(it.count(), 2);
|
||||
|
||||
let it = (0..6).array_chunks::<5>();
|
||||
assert_eq!(it.count(), 1);
|
||||
|
||||
let it = (0..6).array_chunks::<7>();
|
||||
assert_eq!(it.count(), 0);
|
||||
|
||||
let it = (0..6).filter(|x| x % 2 == 0).array_chunks::<2>();
|
||||
assert_eq!(it.count(), 1);
|
||||
|
||||
let it = iter::empty::<i32>().array_chunks::<2>();
|
||||
assert_eq!(it.count(), 0);
|
||||
|
||||
let it = [(); usize::MAX].iter().array_chunks::<2>();
|
||||
assert_eq!(it.count(), usize::MAX / 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_next_and_next_back() {
|
||||
let mut it = (0..11).array_chunks::<3>();
|
||||
assert_eq!(it.next(), Some([0, 1, 2]));
|
||||
assert_eq!(it.next_back(), Some([6, 7, 8]));
|
||||
assert_eq!(it.next(), Some([3, 4, 5]));
|
||||
assert_eq!(it.next_back(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.next_back(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.remainder(), &[9, 10]);
|
||||
assert_eq!(it.remainder_mut(), &[9, 10]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_rev_remainder() {
|
||||
let mut it = (0..11).array_chunks::<4>();
|
||||
{
|
||||
let mut it = it.by_ref().rev();
|
||||
assert_eq!(it.next(), Some([4, 5, 6, 7]));
|
||||
assert_eq!(it.next(), Some([0, 1, 2, 3]));
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
assert_eq!(it.remainder(), &[8, 9, 10]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_try_fold() {
|
||||
let count = Cell::new(0);
|
||||
let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>();
|
||||
let result: Result<_, ()> = it.by_ref().try_fold(0, |acc, _item| Ok(acc + 1));
|
||||
assert_eq!(result, Ok(3));
|
||||
assert_eq!(it.remainder().len(), 1);
|
||||
assert_eq!(count.get(), 9);
|
||||
drop(it);
|
||||
assert_eq!(count.get(), 10);
|
||||
|
||||
let count = Cell::new(0);
|
||||
let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>();
|
||||
let result = it.by_ref().try_fold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) });
|
||||
assert_eq!(result, Err(2));
|
||||
assert_eq!(it.remainder().len(), 0);
|
||||
assert_eq!(count.get(), 9);
|
||||
drop(it);
|
||||
assert_eq!(count.get(), 9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_fold() {
|
||||
let result = (1..11).array_chunks::<3>().fold(0, |acc, [a, b, c]| {
|
||||
assert_eq!(acc + 1, a);
|
||||
assert_eq!(acc + 2, b);
|
||||
assert_eq!(acc + 3, c);
|
||||
acc + 3
|
||||
});
|
||||
assert_eq!(result, 9);
|
||||
|
||||
let count = Cell::new(0);
|
||||
let result =
|
||||
(0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().fold(0, |acc, _item| acc + 1);
|
||||
assert_eq!(result, 3);
|
||||
assert_eq!(count.get(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_try_rfold() {
|
||||
let count = Cell::new(0);
|
||||
let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>();
|
||||
let result: Result<_, ()> = it.try_rfold(0, |acc, _item| Ok(acc + 1));
|
||||
assert_eq!(result, Ok(3));
|
||||
assert_eq!(it.remainder().len(), 1);
|
||||
assert_eq!(count.get(), 9);
|
||||
drop(it);
|
||||
assert_eq!(count.get(), 10);
|
||||
|
||||
let count = Cell::new(0);
|
||||
let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>();
|
||||
let result = it.try_rfold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) });
|
||||
assert_eq!(result, Err(2));
|
||||
assert_eq!(count.get(), 9);
|
||||
drop(it);
|
||||
assert_eq!(count.get(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_array_chunks_rfold() {
|
||||
let result = (1..11).array_chunks::<3>().rfold(0, |acc, [a, b, c]| {
|
||||
assert_eq!(10 - (acc + 1), c);
|
||||
assert_eq!(10 - (acc + 2), b);
|
||||
assert_eq!(10 - (acc + 3), a);
|
||||
acc + 3
|
||||
});
|
||||
assert_eq!(result, 9);
|
||||
|
||||
let count = Cell::new(0);
|
||||
let result =
|
||||
(0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().rfold(0, |acc, _item| acc + 1);
|
||||
assert_eq!(result, 3);
|
||||
assert_eq!(count.get(), 10);
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
mod array_chunks;
|
||||
mod chain;
|
||||
mod cloned;
|
||||
mod copied;
|
||||
@ -183,3 +184,25 @@ impl Clone for CountClone {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CountDrop<'a> {
|
||||
dropped: bool,
|
||||
count: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<'a> CountDrop<'a> {
|
||||
pub fn new(count: &'a Cell<usize>) -> Self {
|
||||
Self { dropped: false, count }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CountDrop<'_> {
|
||||
fn drop(&mut self) {
|
||||
if self.dropped {
|
||||
panic!("double drop");
|
||||
}
|
||||
self.dropped = true;
|
||||
self.count.set(self.count.get() + 1);
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,7 @@
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(int_log)]
|
||||
#![feature(iter_advance_by)]
|
||||
#![feature(iter_array_chunks)]
|
||||
#![feature(iter_collect_into)]
|
||||
#![feature(iter_partition_in_place)]
|
||||
#![feature(iter_intersperse)]
|
||||
|
Loading…
Reference in New Issue
Block a user