diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 45fc5ff80093a..500cbc7db1c9c 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -21,14 +21,16 @@ use borrow::{Borrow, BorrowMut}; use clone::Clone; -use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; +use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering, self}; use convert::{AsRef, AsMut}; use default::Default; use fmt; use hash::{Hash, self}; -use iter::IntoIterator; -use marker::{Copy, Sized, Unsize}; -use option::Option; +use iter::{IntoIterator, Iterator, DoubleEndedIterator, ExactSizeIterator}; +use marker::{Copy, Sized, Unsize, PhantomData}; +use ops::Drop; +use option::Option::{Some, None, self}; +use ptr; use slice::{Iter, IterMut, SliceExt}; /// Utility trait implemented only on arrays of fixed size @@ -62,6 +64,111 @@ unsafe impl> FixedSizeArray for A { } } +/// An iterator that moves out of an array. +#[derive(Debug)] +pub struct IntoIter> { + // 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: Option, + index: usize, + index_back: usize, + _marker: PhantomData, +} + +impl> Drop for IntoIter { + #[inline] + fn drop(&mut self) { + // Drop values that are still alive. + if let Some(array) = self.array.as_mut() { + let slice = array.as_mut_slice(); + for p in &mut slice[self.index..self.index_back] { + unsafe { ptr::drop_in_place(p); } + } + } + + // Prevent the array as a whole from dropping. + unsafe { ptr::write(&mut self.array, None); } + } +} + +impl> Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + if self.len() > 0 { + if let Some(array) = self.array.as_ref() { + let slice = array.as_slice(); + let p = unsafe { slice.get_unchecked(self.index) }; + self.index += 1; + return Some(unsafe { ptr::read(p) }) + } + } + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let len = self.len(); + if len > 0 { + // Drop values prior to the nth. + if let Some(array) = self.array.as_mut() { + let ndrop = cmp::min(n, len); + let slice = array.as_mut_slice(); + for p in &mut slice[self.index..self.index + ndrop] { + unsafe { ptr::drop_in_place(p); } + } + self.index += ndrop; + } + } + self.next() + } + + #[inline] + fn last(mut self) -> Option { + let len = self.len(); + if len > 0 { + self.nth(len - 1) + } else { + None + } + } +} + +impl> DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + if self.len() > 0 { + if let Some(array) = self.array.as_mut() { + self.index_back -= 1; + let slice = array.as_slice(); + let p = unsafe { slice.get_unchecked(self.index_back) }; + return Some(unsafe { ptr::read(p) }) + } + } + None + } +} + +impl> ExactSizeIterator for IntoIter { + #[inline] + fn len(&self) -> usize { + self.index_back - self.index + } +} + macro_rules! __impl_slice_eq1 { ($Lhs: ty, $Rhs: ty) => { __impl_slice_eq1! { $Lhs, $Rhs, Sized } @@ -167,6 +274,20 @@ macro_rules! array_impls { } } + impl IntoIterator for [T; $N] { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { + array: Some(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] } diff --git a/src/libcoretest/array.rs b/src/libcoretest/array.rs index 6af031dee5845..604b82f973e85 100644 --- a/src/libcoretest/array.rs +++ b/src/libcoretest/array.rs @@ -26,3 +26,102 @@ fn fixed_size_array() { assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_array).len(), 0); assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_zero_sized).len(), 0); } + +#[test] +fn test_iterator_nth() { + let v = [0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.clone().into_iter().nth(i).unwrap(), v[i]); + } + assert_eq!(v.clone().into_iter().nth(v.len()), None); + + let mut iter = v.into_iter(); + assert_eq!(iter.nth(2).unwrap(), v[2]); + assert_eq!(iter.nth(1).unwrap(), v[4]); +} + +#[test] +fn test_iterator_last() { + let v = [0, 1, 2, 3, 4]; + assert_eq!(v.into_iter().last().unwrap(), 4); + assert_eq!([0].into_iter().last().unwrap(), 0); +} + +#[test] +fn test_iterator_count() { + let v = [0, 1, 2, 3, 4]; + assert_eq!(v.clone().into_iter().count(), 5); + + let mut iter2 = v.into_iter(); + iter2.next(); + iter2.next(); + assert_eq!(iter2.count(), 3); +} + +#[test] +fn test_iterator_flat_map() { + assert!((0..5).flat_map(|i| [2 * i, 2 * i + 1]).eq(0..10)); +} + +#[test] +fn test_iterator_drops() { + use core::cell::Cell; + + struct R<'a> { + i: &'a Cell, + } + + impl<'a> Drop for R<'a> { + fn drop(&mut self) { + self.i.set(self.i.get() + 1); + } + } + + fn r(i: &Cell) -> R { + R { + i: i + } + } + + fn v(i: &Cell) -> [R; 5] { + [r(i), r(i), r(i), r(i), r(i)] + } + + let i = Cell::new(0); + { + v(&i).into_iter(); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + { + let mut iter = v(&i).into_iter(); + let _x = iter.next(); + assert_eq!(i.get(), 0); + assert_eq!(iter.count(), 4); + assert_eq!(i.get(), 4); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + { + let mut iter = v(&i).into_iter(); + let _x = iter.nth(2); + assert_eq!(i.get(), 2); + let _y = iter.last(); + assert_eq!(i.get(), 3); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + for (index, _x) in v(&i).into_iter().enumerate() { + assert_eq!(i.get(), index); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + for (index, _x) in v(&i).into_iter().rev().enumerate() { + assert_eq!(i.get(), index); + } + assert_eq!(i.get(), 5); +} diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c5850089578cd..d47db8354000d 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -378,7 +378,7 @@ fn init_ids() -> HashMap { "deref-methods", "implementations", "derived_implementations" - ].into_iter().map(|id| (String::from(*id), 1)).collect() + ].into_iter().map(|id| (String::from(id), 1)).collect() } /// This method resets the local table of used ID attributes. This is typically diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index e083605a2acd5..25d4f8fb2ac75 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -262,7 +262,7 @@ mod prim_pointer { } /// /// - `Clone` (only if `T: Copy`) /// - `Debug` -/// - `IntoIterator` (implemented for `&[T; N]` and `&mut [T; N]`) +/// - `IntoIterator` (implemented for `[T; N]`, `&[T; N]`, and `&mut [T; N]`) /// - `PartialEq`, `PartialOrd`, `Ord`, `Eq` /// - `Hash` /// - `AsRef`, `AsMut`