-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
impl<T> IntoIterator for [T; $N] #32871
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<T, A: Unsize<[T]>> FixedSizeArray<T> for A { | |
} | ||
} | ||
|
||
/// An iterator that moves out of an array. | ||
#[derive(Debug)] | ||
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: Option<A>, | ||
index: usize, | ||
index_back: usize, | ||
_marker: PhantomData<T>, | ||
} | ||
|
||
impl<T, A: FixedSizeArray<T>> Drop for IntoIter<T, A> { | ||
#[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<T, A: FixedSizeArray<T>> Iterator for IntoIter<T, A> { | ||
type Item = T; | ||
|
||
#[inline] | ||
fn next(&mut self) -> Option<T> { | ||
if self.len() > 0 { | ||
if let Some(array) = self.array.as_ref() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, it should never be |
||
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<usize>) { | ||
let len = self.len(); | ||
(len, Some(len)) | ||
} | ||
|
||
#[inline] | ||
fn count(self) -> usize { | ||
self.len() | ||
} | ||
|
||
#[inline] | ||
fn nth(&mut self, n: usize) -> Option<T> { | ||
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); } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if one of these destructors panics? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point! I think I prefer inside the loop, just to leak as little as possible, but I could go either way. |
||
} | ||
self.index += ndrop; | ||
} | ||
} | ||
self.next() | ||
} | ||
|
||
#[inline] | ||
fn last(mut self) -> Option<T> { | ||
let len = self.len(); | ||
if len > 0 { | ||
self.nth(len - 1) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
impl<T, A: FixedSizeArray<T>> DoubleEndedIterator for IntoIter<T, A> { | ||
#[inline] | ||
fn next_back(&mut self) -> Option<T> { | ||
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<T, A: FixedSizeArray<T>> ExactSizeIterator for IntoIter<T, A> { | ||
#[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<T> IntoIterator for [T; $N] { | ||
type Item = T; | ||
type IntoIter = IntoIter<T, Self>; | ||
|
||
fn into_iter(self) -> IntoIter<T, Self> { | ||
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] } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually has the same hazard you raised for
nth
-- if there's a panic in one of thedrop_in_place
calls, we won't get to clobberself.array
. And AFAICT a panic in a drop function will still try to drop the struct members, or even locals if I were totake()
the array out early.I think this will need drop-prevention to be an additional wrapper on the array, to be unwound on its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arrayvec has experienced this hazard too, and it uses two drop flags for this reason (nested types).