@@ -17,7 +17,7 @@ struct ArrayBuilder<T, const N: usize> {
17
17
impl < T , const N : usize > ArrayBuilder < T , N > {
18
18
/// Initializes a new, empty `ArrayBuilder`.
19
19
pub fn new ( ) -> Self {
20
- // SAFETY: the validity invariant trivially hold for a zero-length array .
20
+ // SAFETY: The safety invariant of `arr` trivially holds for `len = 0` .
21
21
Self {
22
22
arr : [ ( ) ; N ] . map ( |_| MaybeUninit :: uninit ( ) ) ,
23
23
len : 0 ,
@@ -28,42 +28,124 @@ impl<T, const N: usize> ArrayBuilder<T, N> {
28
28
///
29
29
/// # Panics
30
30
///
31
- /// This panics if `self.len() >= N`.
31
+ /// This panics if `self.len >= N` or if `self.len == usize::MAX `.
32
32
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.
33
+ // PANICS: This will panic if `self.len >= N`.
34
+ // SAFETY: The safety invariant of `self.arr` applies to elements at
35
+ // indices `0..self.len` — not to the element at `self.len`. Writing to
36
+ // the element at index `self.len` therefore does not violate the safety
37
+ // invariant of `self.arr`. Even if this line panics, we have not
38
+ // created any intermediate invalid state.
36
39
self . arr [ self . len ] = MaybeUninit :: new ( value) ;
37
- self . len += 1 ;
40
+ // PANICS: This will panic if `self.len == usize::MAX`.
41
+ // SAFETY: By invariant on `self.arr`, all elements at indicies
42
+ // `0..self.len` are valid. Due to the above write, the element at
43
+ // `self.len` is now also valid. Consequently, all elements at indicies
44
+ // `0..(self.len + 1)` are valid, and `self.len` can be safely
45
+ // incremented without violating `self.arr`'s invariant. It is fine if
46
+ // this increment panics, as we have not created any intermediate
47
+ // invalid state.
48
+ self . len = match self . len . checked_add ( 1 ) {
49
+ Some ( sum) => sum,
50
+ None => panic ! ( "`self.len == usize::MAX`; cannot increment `len`" ) ,
51
+ } ;
38
52
}
39
53
40
- /// Consumes the elements in the `ArrayBuilder` and returns them as an array `[T; N]`.
54
+ /// Consumes the elements in the `ArrayBuilder` and returns them as an array
55
+ /// `[T; N]`.
41
56
///
42
57
/// If `self.len() < N`, this returns `None`.
43
58
pub fn take ( & mut self ) -> Option < [ T ; N ] > {
44
59
if self . len == N {
45
- // Take the array, resetting our length back to zero.
60
+ // SAFETY: Decreasing the value of `self.len` cannot violate the
61
+ // safety invariant on `self.arr`.
46
62
self . len = 0 ;
63
+
64
+ // SAFETY: Since `self.len` is 0, `self.arr` may safely contain
65
+ // uninitialized elements.
47
66
let arr = mem:: replace ( & mut self . arr , [ ( ) ; N ] . map ( |_| MaybeUninit :: uninit ( ) ) ) ;
48
67
49
- // SAFETY: we had len == N, so all elements in arr are valid.
50
- Some ( unsafe { arr. map ( |v| v. assume_init ( ) ) } )
68
+ Some ( arr. map ( |v| {
69
+ // SAFETY: We know that all elements of `arr` are valid because
70
+ // we checked that `len == N`.
71
+ unsafe { v. assume_init ( ) }
72
+ } ) )
51
73
} else {
52
74
None
53
75
}
54
76
}
55
77
}
56
78
57
79
impl < T , const N : usize > Drop for ArrayBuilder < T , N > {
80
+ // We provide a non-trivial `Drop` impl, because the trivial impl would be a
81
+ // no-op; `MaybeUninit<T>` has no innate awareness of its own validity, and
82
+ // so it can only forget its contents. By leveraging the safety invariant of
83
+ // `self.arr`, we do know which elements of `self.arr` are valid, and can
84
+ // selectively run their destructors.
58
85
fn drop ( & mut self ) {
86
+ // Select the valid elements of `self.arr`.
87
+ //
88
+ // LEMMA 1: The elements of `valid` reference the valid elements of
89
+ // `self.arr`.
90
+ //
91
+ // PROOF: `slice::split_at_mut(mid)` produces a pair of slices, the
92
+ // first of which contains the elements at the indices `0..mid`. By
93
+ // invariant on `self.arr`, the elements of `self.arr` at indicies
94
+ // `0..self.len` are valid. Assuming that `slice::split_at_mut` is
95
+ // correctly implemented, the slice `valid` will only reference the
96
+ // valid elements of `self.arr`.
97
+ let ( valid, _) = self . arr . split_at_mut ( self . len ) ;
98
+
99
+ // Cast `valid` from `&[MaybeUninit<T>]` to `&[T]`
100
+ //
101
+ // `align_to_mut` guarantees that the length of the casted slice will be
102
+ // as long as possible within the constraints of the source and
103
+ // destination element types' alignments and sizes. Since
104
+ // `MaybeUninit<T>` and `T` have identical alignments and sizes [1], all
105
+ // elements will be casted and the prefix and suffix components of the
106
+ // return value will be empty and `valid` will contain all of the
107
+ // elements that it did prior to the cast.
108
+ //
109
+ // SAFETY: It is sound to cast a `MaybeUninit<T>` that contains a valid
110
+ // `T` to a `T`. A `MaybeUninit<T>` is guaranteed to have the same size,
111
+ // alignment, and ABI as `T` [1], and by LEMMA 1, `valid` consists only
112
+ // of `MaybeUninit<T>` in the initialized state.
113
+ //
114
+ // [1]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1
115
+ let ( _, valid, _) : ( _ , & mut [ T ] , _ ) = unsafe { valid. align_to_mut :: < T > ( ) } ;
116
+
117
+ // LEMMA 2: `valid_ptr` has exactly the same safety invariants as
118
+ // `valid`.
119
+ //
120
+ // PROOF: We assume that `slice::as_mut_ptr` correctly produces a raw
121
+ // `mut` slice pointing to the same elements as its receiver. Such a
122
+ // pointer will be valid for both reads and writes, be properly aligned,
123
+ // and be non-null. By `mem::forget`ting `valid`, we additionally ensure
124
+ // that `valid_ptr` is the *only* pointer to its referent.
125
+ let valid_ptr = {
126
+ let ptr = valid. as_mut_ptr ( ) ;
127
+ // Move `valid` out of the surrounding scope and immediately drop
128
+ // it. `ptr` is now the only pointer to `valid`'s referent.
129
+ drop ( valid) ;
130
+ ptr
131
+ } ;
132
+
133
+ // Run the destructors of `valid_ptr`'s elements.
134
+ //
135
+ // SAFETY:
136
+ // - `valid_ptr`, by LEMMA 2, is valid for both reads and writes
137
+ // - `valid_ptr`, by LEMMA 2, is properly aligned
138
+ // - `valid_ptr`, by LEMMA 2, is non-null
139
+ // - `valid_ptr`, by LEMMA 2, is valid for dropping, because it is data
140
+ // owned by the `ArrayBuilder` and we place no additional drop-related
141
+ // invariants on it
142
+ // - `valid_ptr`, by LEMMA 2, is the only pointer to its referent, and
143
+ // therefore its referent cannot be concurrently accessed during the
144
+ // execution of `ptr::drop_in_place`.
145
+ // - The referent of `valid_ptr`, which may not be `Copy` is not re-used
146
+ // after the invocation of `ptr::drop_in_place`.
59
147
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) ;
148
+ ptr:: drop_in_place ( valid_ptr) ;
67
149
}
68
150
}
69
151
}
0 commit comments