diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs new file mode 100644 index 00000000000..f9c3f03cbb8 --- /dev/null +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -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 { + array: [MaybeUninit; N], + init: usize, +} + +impl Remainder { + fn new() -> Self { + Self { array: MaybeUninit::uninit_array(), init: 0 } + } + + unsafe fn with_init(array: [MaybeUninit; 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 Clone for Remainder +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 Drop for Remainder { + 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 { + iter: Fuse, + remainder: Remainder, +} + +impl ArrayChunks +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 Iterator for ArrayChunks +where + I: Iterator, +{ + type Item = [I::Item; N]; + + #[inline] + fn next(&mut self) -> Option { + 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) { + 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(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + 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(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 { + /// 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 FrontGuard { + unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { + Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 } + } +} + +impl Drop for FrontGuard { + 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 DoubleEndedIterator for ArrayChunks +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option { + // 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(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + // 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(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 ArrayChunks +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 { + /// 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 BackGuard { + unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { + Self { ptr: MaybeUninit::slice_as_mut_ptr(array), uninit: N } + } +} + +impl Drop for BackGuard { + 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 FusedIterator for ArrayChunks where I: FusedIterator {} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +impl ExactSizeIterator for ArrayChunks +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 TrustedLen for ArrayChunks where I: TrustedLen {} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 916a26e2424..39e7ab87869 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -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; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d5c6aed5b6c..d48e3a52c79 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -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")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 275412b57b5..8bf41ca6f2a 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -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(self) -> ArrayChunks + where + Self: Sized, + { + ArrayChunks::new(self) + } + /// Sums the elements of an iterator. /// /// Takes each element, adds them together, and returns the result. diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs new file mode 100644 index 00000000000..6845c94d364 --- /dev/null +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -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::().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); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index 567d9fe49ca..96539c0c394 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -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, +} + +impl<'a> CountDrop<'a> { + pub fn new(count: &'a Cell) -> 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); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index db94368f6e0..8b4838bb7bc 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)]