Function unarray::mark_initialized

source ·
pub unsafe fn mark_initialized<T, const N: usize>(
    src: [MaybeUninit<T>; N]
) -> [T; N]
Expand description

Convert a [MaybeUninit<T>; N] to a [T; N]

let mut buffer = uninit_buf::<i32, 1000>();

for elem in &mut buffer {
  elem.write(123);
}

let result = unsafe { mark_initialized(buffer) };
assert_eq!(result, [123; 1000])

This largely acts as a workaround to the fact that core::mem::transmute cannot be used with const generic arrays, as it can’t prove they have the same size (even when intuitively they are the same, e.g. [i32; N] and [u32; N]).

This is similar to the nightly-only core::mem::MaybeUninit::array_assume_init

Safety

Internally, this uses core::mem::transmute_copy to convert a [MaybeUninit<T>; N] to [T; N]. As such, you must make sure every element has been initialized before calling this function. If there are uninitialized elements in src, these will be converted to Ts, which is UB. For example:

// ⚠️ This example produces UB ⚠️
let bools = uninit_buf::<bool, 10>();
let uh_oh = unsafe { mark_initialized(bools) };  // UB: creating an invalid instance
if uh_oh[0] {                                    // double UB: reading from unintiailized memory
  // ...
}

Even if you never use a value, it’s still UB. This is especially true for types with core::ops::Drop implementations:

// ⚠️ This example produces UB ⚠️
let strings = uninit_buf::<String, 10>();
let uh_oh = unsafe { mark_initialized(strings) };  // UB: creating an invalid instance

// uh_oh is dropped here, freeing memory at random addresses