The docs for MaybeUninit
give the following snippet for working with uninitialized arrays:
let mut data: [MaybeUninit<Vec<u32>>; 1000] = unsafe {
MaybeUninit::uninit().assume_init()
};
for elem in &mut data[..] {
elem.write(vec![42]);
}
unsafe { mem::transmute::<_, [Vec<u32>; 1000]>(data) }
My understanding is that this is safe because:
- the first
unsafe
block is fine because it's asserting that aMaybeUninit::uninit
is already initialized, but this is fine because this is resolving to a type of[MaybeUninit<_>, N]
, which explicitly doesn't require initialization - the second
unsafe
block is fine because[MaybeUninit<Vec<u32>>; 1000]
is guaranteed to have the same size/alignment as[Vec<u32>; 1000]
, and every element is initialized, so the transmute is fine
I'm trying to extend this pattern to generic types. Roughly speaking, I have a function like this:
fn create_array<T, const N: usize>(f: impl FnMut(usize) -> T) -> [T; N] {
let mut data: [MaybeUninit<T>; N] = unsafe {
MaybeUninit::uninit().assume_init()
}
for (index, slot) in data.iter_mut().enumerate() {
let value = f(index);
slot.write(value);
}
unsafe { transmute_copy::<_, [T; N]>(&data) }
}
(I'm using transmute_copy
since transmute
doesn't seem to work with const generic parameters).
However, I'm worried that this is unsound. In particular, I'm not sure that its sound if f
panics. I have written some tests that panic in f
and run them with Miri, which doesn't seem to find any issues, but I'm still not certain. A friend pointed me towards the UnwindSafe
trait, which seems to suggest that some types can leave data in an inconsistent state when panicking, but it also says that, since it's not an unsafe
trait`, memory safety doesn't depend on it being implemented appropriately.
Am I worrying about nothing, or is there an issue when using generics here? Any advice very much appreciated Thanks!