Skip to content

Commit 68b69ef

Browse files
committed
Implement IntoIterator for Box<[T; N], A>
1 parent d919fb8 commit 68b69ef

1 file changed

Lines changed: 267 additions & 3 deletions

File tree

library/alloc/src/boxed/iter.rs

Lines changed: 267 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
use core::async_iter::AsyncIterator;
2-
use core::iter::FusedIterator;
2+
use core::iter::{self, FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce};
3+
use core::mem::MaybeUninit;
4+
use core::num::NonZero;
5+
use core::ops::IndexRange;
36
use core::pin::Pin;
4-
use core::slice;
57
use core::task::{Context, Poll};
8+
use core::{ptr, slice};
69

710
use crate::alloc::Allocator;
811
#[cfg(not(no_global_oom_handling))]
912
use crate::borrow::Cow;
1013
use crate::boxed::Box;
1114
#[cfg(not(no_global_oom_handling))]
1215
use crate::string::String;
13-
use crate::vec;
1416
#[cfg(not(no_global_oom_handling))]
1517
use crate::vec::Vec;
18+
use crate::{fmt, vec};
1619

1720
#[stable(feature = "rust1", since = "1.0.0")]
1821
impl<I: Iterator + ?Sized, A: Allocator> Iterator for Box<I, A> {
@@ -197,3 +200,264 @@ impl<'a> FromIterator<Cow<'a, str>> for Box<str> {
197200
/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
198201
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
199202
impl<I, const N: usize, A: Allocator> !Iterator for Box<[I; N], A> {}
203+
204+
/// A by-value `Box<[T; N]>` iterator.
205+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
206+
#[rustc_insignificant_dtor]
207+
pub struct BoxedArrayIntoIter<T, const N: usize, A: Allocator> {
208+
/// This is the array we are iterating over.
209+
///
210+
/// Elements with index `i` where `alive.start <= i < alive.end` have not
211+
/// been yielded yet and are valid array entries. Elements with indices `i
212+
/// < alive.start` or `i >= alive.end` have been yielded already and must
213+
/// not be accessed anymore! Those dead elements might even be in a
214+
/// completely uninitialized state!
215+
///
216+
/// So the invariants are:
217+
/// - `data[alive]` is alive (i.e. contains valid elements)
218+
/// - `data[..alive.start]` and `data[alive.end..]` are dead (i.e. the
219+
/// elements were already read and must not be touched anymore!)
220+
data: Box<[MaybeUninit<T>; N], A>,
221+
222+
/// The elements in `data` that have not been yielded yet.
223+
///
224+
/// Invariants:
225+
/// - `alive.end <= N`
226+
///
227+
/// (And the `IndexRange` type requires `alive.start <= alive.end`.)
228+
alive: IndexRange,
229+
}
230+
231+
impl<T, const N: usize, A: Allocator> BoxedArrayIntoIter<T, N, A> {
232+
/// Returns an immutable slice of all elements that have not been yielded
233+
/// yet.
234+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
235+
pub fn as_slice(&self) -> &[T] {
236+
// SAFETY: We know that all elements within `alive` are properly initialized.
237+
unsafe { self.data.get_unchecked(self.alive.clone()).assume_init_ref() }
238+
}
239+
240+
/// Returns a mutable slice of all elements that have not been yielded yet.
241+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
242+
pub fn as_mut_slice(&mut self) -> &mut [T] {
243+
// SAFETY: We know that all elements within `alive` are properly initialized.
244+
unsafe { self.data.get_unchecked_mut(self.alive.clone()).assume_init_mut() }
245+
}
246+
}
247+
248+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
249+
impl<T, const N: usize, A: Allocator> Iterator for BoxedArrayIntoIter<T, N, A> {
250+
type Item = T;
251+
fn next(&mut self) -> Option<Self::Item> {
252+
// Get the next index from the front.
253+
//
254+
// Increasing `alive.start` by 1 maintains the invariant regarding
255+
// `alive`. However, due to this change, for a short time, the alive
256+
// zone is not `data[alive]` anymore, but `data[idx..alive.end]`.
257+
self.alive.next().map(|idx| {
258+
// Read the element from the array.
259+
// SAFETY: `idx` is an index into the former "alive" region of the
260+
// array. Reading this element means that `data[idx]` is regarded as
261+
// dead now (i.e. do not touch). As `idx` was the start of the
262+
// alive-zone, the alive zone is now `data[alive]` again, restoring
263+
// all invariants.
264+
unsafe { self.data.get_unchecked(idx).assume_init_read() }
265+
})
266+
}
267+
268+
fn size_hint(&self) -> (usize, Option<usize>) {
269+
let len = self.len();
270+
(len, Some(len))
271+
}
272+
273+
#[inline]
274+
fn fold<Acc, Fold>(mut self, init: Acc, mut fold: Fold) -> Acc
275+
where
276+
Fold: FnMut(Acc, Self::Item) -> Acc,
277+
{
278+
let data = &mut self.data;
279+
iter::ByRefSized(&mut self.alive).fold(init, |acc, idx| {
280+
// SAFETY: idx is obtained by folding over the `alive` range, which implies the
281+
// value is currently considered alive but as the range is being consumed each value
282+
// we read here will only be read once and then considered dead.
283+
fold(acc, unsafe { data.get_unchecked(idx).assume_init_read() })
284+
})
285+
}
286+
287+
fn count(self) -> usize {
288+
self.len()
289+
}
290+
291+
fn last(mut self) -> Option<Self::Item> {
292+
self.next_back()
293+
}
294+
295+
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
296+
// This also moves the start, which marks them as conceptually "dropped",
297+
// so if anything goes bad then our drop impl won't double-free them.
298+
let range_to_drop = self.alive.take_prefix(n);
299+
let remaining = n - range_to_drop.len();
300+
301+
// SAFETY: These elements are currently initialized, so it's fine to drop them.
302+
unsafe {
303+
self.data.get_unchecked_mut(range_to_drop).assume_init_drop();
304+
}
305+
306+
NonZero::new(remaining).map_or(Ok(()), Err)
307+
}
308+
309+
#[inline]
310+
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
311+
// SAFETY: The caller must provide an idx that is in bound of the remainder.
312+
unsafe { self.data.as_ptr().add(self.alive.start()).add(idx).cast::<T>().read() }
313+
}
314+
}
315+
316+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
317+
impl<T, const N: usize, A: Allocator> DoubleEndedIterator for BoxedArrayIntoIter<T, N, A> {
318+
fn next_back(&mut self) -> Option<Self::Item> {
319+
// Get the next index from the back.
320+
//
321+
// Decreasing `alive.end` by 1 maintains the invariant regarding
322+
// `alive`. However, due to this change, for a short time, the alive
323+
// zone is not `data[alive]` anymore, but `data[alive.start..=idx]`.
324+
self.alive.next_back().map(|idx| {
325+
// Read the element from the array.
326+
// SAFETY: `idx` is an index into the former "alive" region of the
327+
// array. Reading this element means that `data[idx]` is regarded as
328+
// dead now (i.e. do not touch). As `idx` was the end of the
329+
// alive-zone, the alive zone is now `data[alive]` again, restoring
330+
// all invariants.
331+
unsafe { self.data.get_unchecked(idx).assume_init_read() }
332+
})
333+
}
334+
335+
#[inline]
336+
fn rfold<Acc, Fold>(mut self, init: Acc, mut rfold: Fold) -> Acc
337+
where
338+
Fold: FnMut(Acc, Self::Item) -> Acc,
339+
{
340+
let data = &mut self.data;
341+
iter::ByRefSized(&mut self.alive).rfold(init, |acc, idx| {
342+
// SAFETY: idx is obtained by folding over the `alive` range, which implies the
343+
// value is currently considered alive but as the range is being consumed each value
344+
// we read here will only be read once and then considered dead.
345+
rfold(acc, unsafe { data.get_unchecked(idx).assume_init_read() })
346+
})
347+
}
348+
349+
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
350+
// This also moves the end, which marks them as conceptually "dropped",
351+
// so if anything goes bad then our drop impl won't double-free them.
352+
let range_to_drop = self.alive.take_suffix(n);
353+
let remaining = n - range_to_drop.len();
354+
355+
// SAFETY: These elements are currently initialized, so it's fine to drop them.
356+
unsafe {
357+
self.data.get_unchecked_mut(range_to_drop).assume_init_drop();
358+
}
359+
360+
NonZero::new(remaining).map_or(Ok(()), Err)
361+
}
362+
}
363+
364+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
365+
impl<T, const N: usize, A: Allocator> Drop for BoxedArrayIntoIter<T, N, A> {
366+
fn drop(&mut self) {
367+
// SAFETY: This is safe: `as_mut_slice` returns exactly the sub-slice
368+
// of elements that have not been moved out yet and that remain
369+
// to be dropped.
370+
unsafe { ptr::drop_in_place(self.as_mut_slice()) }
371+
}
372+
}
373+
374+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
375+
impl<T, const N: usize, A: Allocator> ExactSizeIterator for BoxedArrayIntoIter<T, N, A> {
376+
fn len(&self) -> usize {
377+
self.alive.len()
378+
}
379+
fn is_empty(&self) -> bool {
380+
self.alive.is_empty()
381+
}
382+
}
383+
384+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
385+
impl<T, const N: usize, A: Allocator> FusedIterator for BoxedArrayIntoIter<T, N, A> {}
386+
387+
// The iterator indeed reports the correct length. The number of "alive"
388+
// elements (that will still be yielded) is the length of the range `alive`.
389+
// This range is decremented in length in either `next` or `next_back`. It is
390+
// always decremented by 1 in those methods, but only if `Some(_)` is returned.
391+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
392+
unsafe impl<T, const N: usize, A: Allocator> TrustedLen for BoxedArrayIntoIter<T, N, A> {}
393+
394+
#[doc(hidden)]
395+
#[unstable(issue = "none", feature = "std_internals")]
396+
#[rustc_unsafe_specialization_marker]
397+
pub trait NonDrop {}
398+
399+
// T: Copy as approximation for !Drop since get_unchecked does not advance self.alive
400+
// and thus we can't implement drop-handling
401+
#[unstable(issue = "none", feature = "std_internals")]
402+
impl<T: Copy> NonDrop for T {}
403+
404+
#[doc(hidden)]
405+
#[unstable(issue = "none", feature = "std_internals")]
406+
unsafe impl<T, const N: usize, A: Allocator> TrustedRandomAccessNoCoerce
407+
for BoxedArrayIntoIter<T, N, A>
408+
where
409+
T: NonDrop,
410+
{
411+
const MAY_HAVE_SIDE_EFFECT: bool = false;
412+
}
413+
414+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
415+
impl<T: Clone, const N: usize, A: Clone + Allocator> Clone for BoxedArrayIntoIter<T, N, A> {
416+
fn clone(&self) -> Self {
417+
// Note, we don't really need to match the exact same alive range, so
418+
// we can just clone into offset 0 regardless of where `self` is.
419+
420+
let mut new = Self {
421+
data: Box::new_in(
422+
[const { MaybeUninit::uninit() }; N],
423+
Box::allocator(&self.data).clone(),
424+
),
425+
alive: IndexRange::zero_to(0),
426+
};
427+
428+
// Clone all alive elements.
429+
for (src, dst) in iter::zip(self.as_slice(), &mut new.data) {
430+
// Write a clone into the new array, then update its alive range.
431+
// If cloning panics, we'll correctly drop the previous items.
432+
dst.write(src.clone());
433+
// This addition cannot overflow as we're iterating a slice
434+
new.alive = IndexRange::zero_to(new.alive.end() + 1);
435+
}
436+
437+
new
438+
}
439+
}
440+
441+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
442+
impl<T: fmt::Debug, const N: usize, A: Allocator> fmt::Debug for BoxedArrayIntoIter<T, N, A> {
443+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444+
// Only print the elements that were not yielded yet: we cannot
445+
// access the yielded elements anymore.
446+
f.debug_tuple("IntoIter").field(&self.as_slice()).finish()
447+
}
448+
}
449+
450+
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
451+
impl<T, const N: usize, A: Allocator> IntoIterator for Box<[T; N], A> {
452+
type IntoIter = BoxedArrayIntoIter<T, N, A>;
453+
type Item = T;
454+
fn into_iter(self) -> BoxedArrayIntoIter<T, N, A> {
455+
// SAFETY: This essentially does a transmute of `[T; N]` -> `[MaybeUninit<T>; N]`,
456+
// this is explicitly allowed as by the `MaybeUninit` docs.
457+
let data = unsafe {
458+
let (ptr, alloc) = Box::into_non_null_with_allocator(self);
459+
Box::from_non_null_in(ptr.cast(), alloc)
460+
};
461+
BoxedArrayIntoIter { data, alive: IndexRange::zero_to(N) }
462+
}
463+
}

0 commit comments

Comments
 (0)