1
1
use core:: mem:: { self , MaybeUninit } ;
2
- use core:: ptr;
3
2
4
3
/// An array of at most `N` elements.
5
4
struct ArrayBuilder < T , const N : usize > {
@@ -17,7 +16,7 @@ struct ArrayBuilder<T, const N: usize> {
17
16
impl < T , const N : usize > ArrayBuilder < T , N > {
18
17
/// Initializes a new, empty `ArrayBuilder`.
19
18
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` .
21
20
Self {
22
21
arr : [ ( ) ; N ] . map ( |_| MaybeUninit :: uninit ( ) ) ,
23
22
len : 0 ,
@@ -28,50 +27,96 @@ impl<T, const N: usize> ArrayBuilder<T, N> {
28
27
///
29
28
/// # Panics
30
29
///
31
- /// This panics if `self.len() >= N`.
30
+ /// This panics if `self.len >= N` or if `self.len == usize::MAX `.
32
31
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.
36
38
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
+ } ;
38
51
}
39
52
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]`.
41
55
///
42
56
/// If `self.len() < N`, this returns `None`.
43
57
pub fn take ( & mut self ) -> Option < [ T ; N ] > {
44
58
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`.
46
61
self . len = 0 ;
62
+
63
+ // SAFETY: Since `self.len` is 0, `self.arr` may safely contain
64
+ // uninitialized elements.
47
65
let arr = mem:: replace ( & mut self . arr , [ ( ) ; N ] . map ( |_| MaybeUninit :: uninit ( ) ) ) ;
48
66
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
+ } ) )
51
72
} else {
52
73
None
53
74
}
54
75
}
55
76
}
56
77
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
+
57
89
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.
58
95
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) }
68
99
}
69
100
}
70
101
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
+
71
116
/// 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 ] >
73
118
where
74
- I : Iterator < Item = T > ,
119
+ I : Iterator ,
75
120
{
76
121
let mut builder = ArrayBuilder :: new ( ) ;
77
122
for _ in 0 ..N {
0 commit comments