Skip to content

Commit

Permalink
Auto merge of #49000 - cuviper:array-intoiter, r=<try>
Browse files Browse the repository at this point in the history
impl IntoIterator for arrays

This allows an array to move its values out through iteration.

This was attempted once before in #32871, but closed because the `IntoIter<T, [T; $N]>` type is not something we would want to stabilize.  However, RFC 2000's const generics (#44580) are now on the horizon, so we can plan on changing this to `IntoIter<T, const N: usize>` before stabilization.

Adding the `impl IntoIterator` now will allows folks to go ahead and iterate arrays in stable code.  They just won't be able to name the `array::IntoIter` type or use its inherent `as_slice`/`as_mut_slice` methods until they've stabilized.

Quite a few iterator examples were already using `.into_iter()` on arrays, getting auto-deref'ed to the slice iterator.  These were easily fixed by calling `.iter()` instead, but it shows that this might cause a lot of breaking changes in the wild, and we'll need a crater run to evaluate this.  Outside of examples, there was only one instance of in-tree code that had a problem.

Fixes #25725.

r? @alexcrichton
  • Loading branch information
bors committed Mar 14, 2018
2 parents fab632f + 032f93b commit be75eb3
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 be75eb3

Please sign in to comment.