Skip to content

Commit f1ca4a5

Browse files
committed
Implement IntoIterator for Box<[T; N], A>
1 parent 3600423 commit f1ca4a5

File tree

1 file changed

+275
-3
lines changed

1 file changed

+275
-3
lines changed

library/alloc/src/boxed/iter.rs

+275-3
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> {
@@ -208,6 +211,275 @@ impl<'a, const N: usize, I, A: Allocator> !Iterator for &'a Box<[I; N], A> {}
208211
#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
209212
impl<'a, const N: usize, I, A: Allocator> !Iterator for &'a mut Box<[I; N], A> {}
210213

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

0 commit comments

Comments
 (0)