diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000000..c88d053d00 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,97 @@ +use nodrop::NoDrop; +use std::cmp; +use std::ptr; +use super::{GenericArray, ArrayLength}; + +/// An iterator that moves out of a `GenericArray` +pub struct GenericArrayIter> { + // Invariants: index <= index_back <= N + // Only values in array[index..index_back] are alive at any given time. + // Values from array[..index] and array[index_back..] are already moved/dropped. + array: NoDrop>, + index: usize, + index_back: usize, +} + +impl IntoIterator for GenericArray where N: ArrayLength { + type Item = T; + type IntoIter = GenericArrayIter; + + fn into_iter(self) -> Self::IntoIter { + GenericArrayIter { + array: NoDrop::new(self), + index: 0, + index_back: N::to_usize(), + } + } +} + +impl Drop for GenericArrayIter where N: ArrayLength { + fn drop(&mut self) { + // Drop values that are still alive. + for p in &mut self.array[self.index..self.index_back] { + unsafe { ptr::drop_in_place(p); } + } + } +} + +impl Iterator for GenericArrayIter where N: ArrayLength { + type Item = T; + + fn next(&mut self) -> Option { + if self.len() > 0 { + unsafe { + let p = self.array.get_unchecked(self.index); + self.index += 1; + Some(ptr::read(p)) + } + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } + + fn count(self) -> usize { + self.len() + } + + fn nth(&mut self, n: usize) -> Option { + // First consume values prior to the nth. + let ndrop = cmp::min(n, self.len()); + for p in &mut self.array[self.index..self.index + ndrop] { + self.index += 1; + unsafe { ptr::drop_in_place(p); } + } + + self.next() + } + + fn last(mut self) -> Option { + // Note, everything else will correctly drop first as `self` leaves scope. + self.next_back() + } +} + +impl DoubleEndedIterator for GenericArrayIter where N: ArrayLength { + fn next_back(&mut self) -> Option { + if self.len() > 0 { + self.index_back -= 1; + unsafe { + let p = self.array.get_unchecked(self.index_back); + Some(ptr::read(p)) + } + } else { + None + } + } +} + +impl ExactSizeIterator for GenericArrayIter where N: ArrayLength { + fn len(&self) -> usize { + self.index_back - self.index + } +} diff --git a/src/lib.rs b/src/lib.rs index f43c2acf9e..04ed38f98a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ extern crate core as std; extern crate typenum; extern crate nodrop; pub mod arr; +pub mod iter; +pub use iter::GenericArrayIter; use nodrop::NoDrop; use typenum::uint::{Unsigned, UTerm, UInt}; use typenum::bit::{B0, B1}; diff --git a/tests/mod.rs b/tests/mod.rs index a392982506..d4cd50684a 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -50,3 +50,8 @@ fn test_arr() { let test: GenericArray = arr![u32; 1, 2, 3]; assert_eq!(test[1], 2); } + +#[test] +fn test_iter_flat_map() { + assert!((0..5).flat_map(|i| arr![i32; 2 * i, 2 * i + 1]).eq(0..10)); +}