Skip to content

Commit

Permalink
impl<T> IntoIterator for [T; $N]
Browse files Browse the repository at this point in the history
This allows an array to move its values out through iteration.  I find
this especially handy for `flat_map`, to expand one item into several
without having to allocate a `Vec`, like one of the new tests:

    fn test_iterator_flat_map() {
        assert!((0..5).flat_map(|i| [2 * i, 2 * i + 1]).eq(0..10));
    }

Note the array must be moved for the iterator to own it, so you probably
don't want this for large `T` or very many items.  But for small arrays,
it should be faster than bothering with a vector and the heap.
  • Loading branch information
cuviper committed Mar 13, 2018
1 parent c290857 commit 032f93b
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 26 deletions.
189 changes: 188 additions & 1 deletion src/libcore/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ use cmp::Ordering;
use convert::TryFrom;
use fmt;
use hash::{Hash, self};
use marker::Unsize;
use iter::{FusedIterator, TrustedLen};
use marker::{PhantomData, Unsize};
use mem::{ManuallyDrop, self};
use ptr;
use slice::{Iter, IterMut};

/// Utility trait implemented only on arrays of fixed size
Expand Down Expand Up @@ -52,6 +55,7 @@ unsafe impl<T, A: Unsize<[T]>> FixedSizeArray<T> for A {
fn as_slice(&self) -> &[T] {
self
}

#[inline]
fn as_mut_slice(&mut self) -> &mut [T] {
self
Expand Down Expand Up @@ -210,6 +214,21 @@ macro_rules! array_impls {
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T> IntoIterator for [T; $N] {
type Item = T;
type IntoIter = IntoIter<T, Self>;

fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
array: ManuallyDrop::new(self),
index: 0,
index_back: $N,
_marker: PhantomData,
}
}
}

// NOTE: some less important impls are omitted to reduce code bloat
__impl_slice_eq1! { [A; $N], [B; $N] }
__impl_slice_eq2! { [A; $N], [B] }
Expand Down Expand Up @@ -285,3 +304,171 @@ macro_rules! array_impl_default {
}

array_impl_default!{32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}


/// An iterator that moves out of an array.
///
/// This `struct` is created by the `into_iter` method on [arrays]
/// (provided by the [`IntoIterator`] trait).
///
/// [arrays]: ../../std/primitive.array.html
/// [`IntoIterator`]: ../../std/iter/trait.IntoIterator.html
#[unstable(feature = "array_into_iter", issue = "0")]
pub struct IntoIter<T, A: FixedSizeArray<T>> {
// Invariants: index <= index_back <= array.len()
// 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: ManuallyDrop<A>,
index: usize,
index_back: usize,
_marker: PhantomData<T>,
}

impl<T, A: FixedSizeArray<T>> IntoIter<T, A> {
/// Returns the remaining items of this iterator as a slice.
///
/// # Examples
///
/// ```
/// #![feature(array_into_iter)]
///
/// # fn main() {
/// let array = ['a', 'b', 'c'];
/// let mut into_iter = array.into_iter();
/// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']);
/// let _ = into_iter.next().unwrap();
/// assert_eq!(into_iter.as_slice(), &['b', 'c']);
/// # }
/// ```
#[inline]
#[unstable(feature = "array_into_iter", issue = "0")]
pub fn as_slice(&self) -> &[T] {
&self.array.as_slice()[self.index..self.index_back]
}

/// Returns the remaining items of this iterator as a mutable slice.
///
/// # Examples
///
/// ```
/// #![feature(array_into_iter)]
///
/// # fn main() {
/// let array = ['a', 'b', 'c'];
/// let mut into_iter = array.into_iter();
/// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']);
/// into_iter.as_mut_slice()[2] = 'z';
/// assert_eq!(into_iter.next().unwrap(), 'a');
/// assert_eq!(into_iter.next().unwrap(), 'b');
/// assert_eq!(into_iter.next().unwrap(), 'z');
/// # }
/// ```
#[inline]
#[unstable(feature = "array_into_iter", issue = "0")]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.array.as_mut_slice()[self.index..self.index_back]
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T, A: FixedSizeArray<T>> Drop for IntoIter<T, A> {
#[inline]
fn drop(&mut self) {
// Drop values that are still alive.
for p in self.as_mut_slice() {
unsafe { ptr::drop_in_place(p); }
}
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T: Clone, A: FixedSizeArray<T>> Clone for IntoIter<T, A> {
fn clone(&self) -> Self {
unsafe {
let mut iter = Self {
array: ManuallyDrop::new(mem::uninitialized()),
index: 0,
index_back: 0,
_marker: PhantomData,
};

// Clone values that are still alive.
for (dst, src) in iter.array.as_mut_slice().iter_mut().zip(self.as_slice()) {
ptr::write(dst, src.clone());
iter.index_back += 1;
}

iter
}
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T: fmt::Debug, A: FixedSizeArray<T>> fmt::Debug for IntoIter<T, A> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("IntoIter")
.field(&self.as_slice())
.finish()
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T, A: FixedSizeArray<T>> Iterator for IntoIter<T, A> {
type Item = T;

#[inline]
fn next(&mut self) -> Option<T> {
if self.index < self.index_back {
let p = &self.array.as_slice()[self.index];
self.index += 1;
unsafe { Some(ptr::read(p)) }
} else {
None
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}

#[inline]
fn count(self) -> usize {
self.len()
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T, A: FixedSizeArray<T>> DoubleEndedIterator for IntoIter<T, A> {
#[inline]
fn next_back(&mut self) -> Option<T> {
if self.index < self.index_back {
self.index_back -= 1;
let p = &self.array.as_slice()[self.index_back];
unsafe { Some(ptr::read(p)) }
} else {
None
}
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T, A: FixedSizeArray<T>> ExactSizeIterator for IntoIter<T, A> {
#[inline]
fn len(&self) -> usize {
self.index_back - self.index
}

#[inline]
fn is_empty(&self) -> bool {
self.index_back == self.index
}
}

#[unstable(feature = "array_into_iter", issue = "0")]
impl<T, A: FixedSizeArray<T>> FusedIterator for IntoIter<T, A> {}

// #[unstable(feature = "trusted_len", issue = "37572")]
#[unstable(feature = "array_into_iter", issue = "0")]
unsafe impl<T, A: FixedSizeArray<T>> TrustedLen for IntoIter<T, A> {}
32 changes: 16 additions & 16 deletions src/libcore/iter/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ pub trait Iterator {
/// ```
/// #![feature(iterator_step_by)]
/// let a = [0, 1, 2, 3, 4, 5];
/// let mut iter = a.into_iter().step_by(2);
/// let mut iter = a.iter().step_by(2);
///
/// assert_eq!(iter.next(), Some(&0));
/// assert_eq!(iter.next(), Some(&2));
Expand Down Expand Up @@ -461,7 +461,7 @@ pub trait Iterator {
/// ```
/// let a = [1, 2, 3];
///
/// let mut iter = a.into_iter().map(|x| 2 * x);
/// let mut iter = a.iter().map(|x| 2 * x);
///
/// assert_eq!(iter.next(), Some(2));
/// assert_eq!(iter.next(), Some(4));
Expand Down Expand Up @@ -550,7 +550,7 @@ pub trait Iterator {
/// ```
/// let a = [0i32, 1, 2];
///
/// let mut iter = a.into_iter().filter(|x| x.is_positive());
/// let mut iter = a.iter().filter(|x| x.is_positive());
///
/// assert_eq!(iter.next(), Some(&1));
/// assert_eq!(iter.next(), Some(&2));
Expand All @@ -564,7 +564,7 @@ pub trait Iterator {
/// ```
/// let a = [0, 1, 2];
///
/// let mut iter = a.into_iter().filter(|x| **x > 1); // need two *s!
/// let mut iter = a.iter().filter(|x| **x > 1); // need two *s!
///
/// assert_eq!(iter.next(), Some(&2));
/// assert_eq!(iter.next(), None);
Expand All @@ -576,7 +576,7 @@ pub trait Iterator {
/// ```
/// let a = [0, 1, 2];
///
/// let mut iter = a.into_iter().filter(|&x| *x > 1); // both & and *
/// let mut iter = a.iter().filter(|&x| *x > 1); // both & and *
///
/// assert_eq!(iter.next(), Some(&2));
/// assert_eq!(iter.next(), None);
Expand All @@ -587,7 +587,7 @@ pub trait Iterator {
/// ```
/// let a = [0, 1, 2];
///
/// let mut iter = a.into_iter().filter(|&&x| x > 1); // two &s
/// let mut iter = a.iter().filter(|&&x| x > 1); // two &s
///
/// assert_eq!(iter.next(), Some(&2));
/// assert_eq!(iter.next(), None);
Expand Down Expand Up @@ -767,7 +767,7 @@ pub trait Iterator {
/// ```
/// let a = [-1i32, 0, 1];
///
/// let mut iter = a.into_iter().skip_while(|x| x.is_negative());
/// let mut iter = a.iter().skip_while(|x| x.is_negative());
///
/// assert_eq!(iter.next(), Some(&0));
/// assert_eq!(iter.next(), Some(&1));
Expand All @@ -781,7 +781,7 @@ pub trait Iterator {
/// ```
/// let a = [-1, 0, 1];
///
/// let mut iter = a.into_iter().skip_while(|x| **x < 0); // need two *s!
/// let mut iter = a.iter().skip_while(|x| **x < 0); // need two *s!
///
/// assert_eq!(iter.next(), Some(&0));
/// assert_eq!(iter.next(), Some(&1));
Expand All @@ -793,7 +793,7 @@ pub trait Iterator {
/// ```
/// let a = [-1, 0, 1, -2];
///
/// let mut iter = a.into_iter().skip_while(|x| **x < 0);
/// let mut iter = a.iter().skip_while(|x| **x < 0);
///
/// assert_eq!(iter.next(), Some(&0));
/// assert_eq!(iter.next(), Some(&1));
Expand Down Expand Up @@ -828,7 +828,7 @@ pub trait Iterator {
/// ```
/// let a = [-1i32, 0, 1];
///
/// let mut iter = a.into_iter().take_while(|x| x.is_negative());
/// let mut iter = a.iter().take_while(|x| x.is_negative());
///
/// assert_eq!(iter.next(), Some(&-1));
/// assert_eq!(iter.next(), None);
Expand All @@ -841,7 +841,7 @@ pub trait Iterator {
/// ```
/// let a = [-1, 0, 1];
///
/// let mut iter = a.into_iter().take_while(|x| **x < 0); // need two *s!
/// let mut iter = a.iter().take_while(|x| **x < 0); // need two *s!
///
/// assert_eq!(iter.next(), Some(&-1));
/// assert_eq!(iter.next(), None);
Expand All @@ -852,7 +852,7 @@ pub trait Iterator {
/// ```
/// let a = [-1, 0, 1, -2];
///
/// let mut iter = a.into_iter().take_while(|x| **x < 0);
/// let mut iter = a.iter().take_while(|x| **x < 0);
///
/// assert_eq!(iter.next(), Some(&-1));
///
Expand All @@ -867,7 +867,7 @@ pub trait Iterator {
///
/// ```
/// let a = [1, 2, 3, 4];
/// let mut iter = a.into_iter();
/// let mut iter = a.iter();
///
/// let result: Vec<i32> = iter.by_ref()
/// .take_while(|n| **n != 3)
Expand Down Expand Up @@ -1229,7 +1229,7 @@ pub trait Iterator {
/// ```
/// let a = [1, 2, 3];
///
/// let iter = a.into_iter();
/// let iter = a.iter();
///
/// let sum: i32 = iter.take(5).fold(0, |acc, i| acc + i );
///
Expand All @@ -1242,7 +1242,7 @@ pub trait Iterator {
/// // let's try that again
/// let a = [1, 2, 3];
///
/// let mut iter = a.into_iter();
/// let mut iter = a.iter();
///
/// // instead, we add in a .by_ref()
/// let sum: i32 = iter.by_ref().take(2).fold(0, |acc, i| acc + i );
Expand Down Expand Up @@ -1386,7 +1386,7 @@ pub trait Iterator {
/// let a = [1, 2, 3];
///
/// let (even, odd): (Vec<i32>, Vec<i32>) = a
/// .into_iter()
/// .iter()
/// .partition(|&n| n % 2 == 0);
///
/// assert_eq!(even, vec![2]);
Expand Down
Loading

0 comments on commit 032f93b

Please sign in to comment.