Skip to content

Commit 9911308

Browse files
committed
next_array: Revise safety comments, Drop, and push
1 parent 2c3af5c commit 9911308

File tree

3 files changed

+74
-29
lines changed

3 files changed

+74
-29
lines changed

src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1928,9 +1928,9 @@ pub trait Itertools: Iterator {
19281928
///
19291929
/// assert_eq!(Some([1, 2]), iter.next_array());
19301930
/// ```
1931-
fn next_array<T, const N: usize>(&mut self) -> Option<[T; N]>
1931+
fn next_array<const N: usize>(&mut self) -> Option<[Self::Item; N]>
19321932
where
1933-
Self: Sized + Iterator<Item = T>,
1933+
Self: Sized,
19341934
{
19351935
next_array::next_array(self)
19361936
}
@@ -1952,9 +1952,9 @@ pub trait Itertools: Iterator {
19521952
/// panic!("Expected two elements")
19531953
/// }
19541954
/// ```
1955-
fn collect_array<T, const N: usize>(mut self) -> Option<[T; N]>
1955+
fn collect_array<const N: usize>(mut self) -> Option<[Self::Item; N]>
19561956
where
1957-
Self: Sized + Iterator<Item = T>,
1957+
Self: Sized,
19581958
{
19591959
self.next_array().filter(|_| self.next().is_none())
19601960
}

src/next_array.rs

+67-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use core::mem::{self, MaybeUninit};
2-
use core::ptr;
32

43
/// An array of at most `N` elements.
54
struct ArrayBuilder<T, const N: usize> {
@@ -17,7 +16,7 @@ struct ArrayBuilder<T, const N: usize> {
1716
impl<T, const N: usize> ArrayBuilder<T, N> {
1817
/// Initializes a new, empty `ArrayBuilder`.
1918
pub fn new() -> Self {
20-
// SAFETY: the validity invariant trivially hold for a zero-length array.
19+
// SAFETY: The safety invariant of `arr` trivially holds for `len = 0`.
2120
Self {
2221
arr: [(); N].map(|_| MaybeUninit::uninit()),
2322
len: 0,
@@ -28,50 +27,96 @@ impl<T, const N: usize> ArrayBuilder<T, N> {
2827
///
2928
/// # Panics
3029
///
31-
/// This panics if `self.len() >= N`.
30+
/// This panics if `self.len >= N` or if `self.len == usize::MAX`.
3231
pub fn push(&mut self, value: T) {
33-
// SAFETY: we maintain the invariant here that arr[..len] is valid.
34-
// Indexing with self.len also ensures self.len < N, and thus <= N after
35-
// the increment.
32+
// PANICS: This will panic if `self.len >= N`.
33+
// SAFETY: The safety invariant of `self.arr` applies to elements at
34+
// indices `0..self.len` — not to the element at `self.len`. Writing to
35+
// the element at index `self.len` therefore does not violate the safety
36+
// invariant of `self.arr`. Even if this line panics, we have not
37+
// created any intermediate invalid state.
3638
self.arr[self.len] = MaybeUninit::new(value);
37-
self.len += 1;
39+
// PANICS: This will panic if `self.len == usize::MAX`.
40+
// SAFETY: By invariant on `self.arr`, all elements at indicies
41+
// `0..self.len` are valid. Due to the above write, the element at
42+
// `self.len` is now also valid. Consequently, all elements at indicies
43+
// `0..(self.len + 1)` are valid, and `self.len` can be safely
44+
// incremented without violating `self.arr`'s invariant. It is fine if
45+
// this increment panics, as we have not created any intermediate
46+
// invalid state.
47+
self.len = match self.len.checked_add(1) {
48+
Some(sum) => sum,
49+
None => panic!("`self.len == usize::MAX`; cannot increment `len`"),
50+
};
3851
}
3952

40-
/// Consumes the elements in the `ArrayBuilder` and returns them as an array `[T; N]`.
53+
/// Consumes the elements in the `ArrayBuilder` and returns them as an array
54+
/// `[T; N]`.
4155
///
4256
/// If `self.len() < N`, this returns `None`.
4357
pub fn take(&mut self) -> Option<[T; N]> {
4458
if self.len == N {
45-
// Take the array, resetting our length back to zero.
59+
// SAFETY: Decreasing the value of `self.len` cannot violate the
60+
// safety invariant on `self.arr`.
4661
self.len = 0;
62+
63+
// SAFETY: Since `self.len` is 0, `self.arr` may safely contain
64+
// uninitialized elements.
4765
let arr = mem::replace(&mut self.arr, [(); N].map(|_| MaybeUninit::uninit()));
4866

49-
// SAFETY: we had len == N, so all elements in arr are valid.
50-
Some(unsafe { arr.map(|v| v.assume_init()) })
67+
Some(arr.map(|v| {
68+
// SAFETY: We know that all elements of `arr` are valid because
69+
// we checked that `len == N`.
70+
unsafe { v.assume_init() }
71+
}))
5172
} else {
5273
None
5374
}
5475
}
5576
}
5677

78+
impl<T, const N: usize> AsMut<[T]> for ArrayBuilder<T, N> {
79+
fn as_mut(&mut self) -> &mut [T] {
80+
let valid = &mut self.arr[..self.len];
81+
// SAFETY: By invariant on `self.arr`, the elements of `self.arr` at
82+
// indices `0..self.len` are in a valid state. Since `valid` references
83+
// only these elements, the safety precondition of
84+
// `slice_assume_init_mut` is satisfied.
85+
unsafe { slice_assume_init_mut(valid) }
86+
}
87+
}
88+
5789
impl<T, const N: usize> Drop for ArrayBuilder<T, N> {
90+
// We provide a non-trivial `Drop` impl, because the trivial impl would be a
91+
// no-op; `MaybeUninit<T>` has no innate awareness of its own validity, and
92+
// so it can only forget its contents. By leveraging the safety invariant of
93+
// `self.arr`, we do know which elements of `self.arr` are valid, and can
94+
// selectively run their destructors.
5895
fn drop(&mut self) {
59-
unsafe {
60-
// SAFETY: arr[..len] is valid, so must be dropped. First we create
61-
// a pointer to this valid slice, then drop that slice in-place.
62-
// The cast from *mut MaybeUninit<T> to *mut T is always sound by
63-
// the layout guarantees of MaybeUninit.
64-
let ptr_to_first: *mut MaybeUninit<T> = self.arr.as_mut_ptr();
65-
let ptr_to_slice = ptr::slice_from_raw_parts_mut(ptr_to_first.cast::<T>(), self.len);
66-
ptr::drop_in_place(ptr_to_slice);
67-
}
96+
let valid = self.as_mut();
97+
// SAFETY: TODO
98+
unsafe { core::ptr::drop_in_place(valid) }
6899
}
69100
}
70101

102+
/// Assuming all the elements are initialized, get a mutable slice to them.
103+
///
104+
/// # Safety
105+
///
106+
/// The caller guarantees that the elements `T` referenced by `slice` are in a
107+
/// valid state.
108+
unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
109+
// SAFETY: Casting `&mut [MaybeUninit<T>]` to `&mut [T]` is sound, because
110+
// `MaybeUninit<T>` is guaranteed to have the same size, alignment and ABI
111+
// as `T`, and because the caller has guaranteed that `slice` is in the
112+
// valid state.
113+
unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) }
114+
}
115+
71116
/// Equivalent to `it.next_array()`.
72-
pub fn next_array<I, T, const N: usize>(it: &mut I) -> Option<[T; N]>
117+
pub fn next_array<I, const N: usize>(it: &mut I) -> Option<[I::Item; N]>
73118
where
74-
I: Iterator<Item = T>,
119+
I: Iterator,
75120
{
76121
let mut builder = ArrayBuilder::new();
77122
for _ in 0..N {

tests/test_core.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ fn next_array() {
380380
assert_eq!(iter.next_array(), Some([]));
381381
assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([1, 2]));
382382
assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([3, 4]));
383-
assert_eq!(iter.next_array::<_, 2>(), None);
383+
assert_eq!(iter.next_array::<2>(), None);
384384
}
385385

386386
#[test]
@@ -391,9 +391,9 @@ fn collect_array() {
391391

392392
let v = [1];
393393
let iter = v.iter().cloned();
394-
assert_eq!(iter.collect_array::<_, 2>(), None);
394+
assert_eq!(iter.collect_array::<2>(), None);
395395

396396
let v = [1, 2, 3];
397397
let iter = v.iter().cloned();
398-
assert_eq!(iter.collect_array::<_, 2>(), None);
398+
assert_eq!(iter.collect_array::<2>(), None);
399399
}

0 commit comments

Comments
 (0)