Skip to content

Commit 6e30fbd

Browse files
committed
Implement IntoIterator for Box<[T; N], A>
1 parent bdd3a2d commit 6e30fbd

2 files changed

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

library/core/src/ops/index_range.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ impl IndexRange {
3333
}
3434

3535
#[inline]
36-
pub(crate) const fn zero_to(end: usize) -> Self {
36+
pub const fn zero_to(end: usize) -> Self {
3737
IndexRange { start: 0, end }
3838
}
3939

4040
#[inline]
41-
pub(crate) const fn start(&self) -> usize {
41+
pub const fn start(&self) -> usize {
4242
self.start
4343
}
4444

4545
#[inline]
46-
pub(crate) const fn end(&self) -> usize {
46+
pub const fn end(&self) -> usize {
4747
self.end
4848
}
4949

@@ -84,7 +84,7 @@ impl IndexRange {
8484
///
8585
/// This is designed to help implement `Iterator::advance_by`.
8686
#[inline]
87-
pub(crate) fn take_prefix(&mut self, n: usize) -> Self {
87+
pub fn take_prefix(&mut self, n: usize) -> Self {
8888
let mid = if n <= self.len() {
8989
// SAFETY: We just checked that this will be between start and end,
9090
// and thus the addition cannot overflow.
@@ -104,7 +104,7 @@ impl IndexRange {
104104
///
105105
/// This is designed to help implement `Iterator::advance_back_by`.
106106
#[inline]
107-
pub(crate) fn take_suffix(&mut self, n: usize) -> Self {
107+
pub fn take_suffix(&mut self, n: usize) -> Self {
108108
let mid = if n <= self.len() {
109109
// SAFETY: We just checked that this will be between start and end,
110110
// and thus the subtraction cannot overflow.

0 commit comments

Comments
 (0)