|
1 | 1 | use crate::iter::FusedIterator; |
2 | | -use crate::mem::{MaybeUninit, SizedTypeProperties}; |
| 2 | +use crate::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; |
3 | 3 | use crate::{fmt, ptr}; |
4 | 4 |
|
5 | 5 | /// An iterator over the mapped windows of another iterator. |
@@ -30,14 +30,19 @@ struct MapWindowsInner<I: Iterator, const N: usize> { |
30 | 30 | buffer: Option<Buffer<I::Item, N>>, |
31 | 31 | } |
32 | 32 |
|
33 | | -// `Buffer` uses two times of space to reduce moves among the iterations. |
34 | | -// `Buffer<T, N>` is semantically `[MaybeUninit<T>; 2 * N]`. However, due |
35 | | -// to limitations of const generics, we use this different type. Note that |
36 | | -// it has the same underlying memory layout. |
| 33 | +/// `Buffer<T, N>` is semantically `[MaybeUninit<T>; 2 * N]`. This helps |
| 34 | +/// reduce moves while iterating. However, due |
| 35 | +/// to limitations of const generics, we use this different type. Note that |
| 36 | +/// it has the same underlying memory layout. |
| 37 | +/// |
| 38 | +/// # Safety invariant |
| 39 | +/// |
| 40 | +/// `self.buffer[self.start..self.start + N]` must be initialized, |
| 41 | +/// with all other elements being uninitialized. This also |
| 42 | +/// implies that `self.start <= N`. |
| 43 | +// |
| 44 | +// FIXME make these unsafe fields once that feature is ready |
37 | 45 | struct Buffer<T, const N: usize> { |
38 | | - // Invariant: `self.buffer[self.start..self.start + N]` is initialized, |
39 | | - // with all other elements being uninitialized. This also |
40 | | - // implies that `self.start <= N`. |
41 | 46 | buffer: [[MaybeUninit<T>; N]; 2], |
42 | 47 | start: usize, |
43 | 48 | } |
@@ -194,12 +199,18 @@ impl<T, const N: usize> Buffer<T, N> { |
194 | 199 |
|
195 | 200 | impl<T: Clone, const N: usize> Clone for Buffer<T, N> { |
196 | 201 | fn clone(&self) -> Self { |
197 | | - let mut buffer = Buffer { |
| 202 | + // Use `ManuallyDrop` until buffer is fully written to avoid dropping uninitialized elements on panic. |
| 203 | + // (See `Buffer` rustdoc for safety invariant) |
| 204 | + let mut buffer = ManuallyDrop::new(Buffer { |
198 | 205 | buffer: [[const { MaybeUninit::uninit() }; N], [const { MaybeUninit::uninit() }; N]], |
199 | 206 | start: self.start, |
200 | | - }; |
| 207 | + }); |
| 208 | + |
| 209 | + // `clone()` could panic; `ManuallyDrop` guards against that. |
201 | 210 | buffer.as_uninit_array_mut().write(self.as_array_ref().clone()); |
202 | | - buffer |
| 211 | + |
| 212 | + // We initialized the buffer above, so we are good now |
| 213 | + ManuallyDrop::into_inner(buffer) |
203 | 214 | } |
204 | 215 | } |
205 | 216 |
|
|
0 commit comments