`MaybeUninit` with arrays on stable, is this code sound?

Is this code sound? At the moment it requires a custom extremely unsafe transmute function. So I was wondering if I have done this correctly.

Playground

use std::mem;
use std::mem::MaybeUninit;
use std::ptr;

/// Size-heterogeneous transmutation (seriously unsafe!)
///
/// See `transmute` crate.
#[inline]
unsafe fn transmute<A, B>(a: A) -> B {
    let b = ptr::read(&a as *const A as *const B);
    mem::forget(a);
    b
}

fn init_data<F, T, const M: usize, const N: usize>(mut f: F) -> [[T; M]; N]
where
    F: FnMut() -> T,
{
    // Safety: `MaybeUninit`s do not require initialization.
    let mut data: [[MaybeUninit<T>; M]; N] = unsafe { MaybeUninit::uninit().assume_init() };

    for i in 0..N {
        for j in 0..M {
            data[i][j] = MaybeUninit::new(f());
        }
    }

    // Safety: we initialized everything.
    unsafe { transmute::<_, [[T; M]; N]>(data) }
}

fn main() {
    let data: [[_; 5]; 10] = init_data(|| vec!["is", "this", "sound", "?"]);
    println!("{:#?}", data);
}

Yes, it's fine. The main problem is that if f panics, then you don't run destructors of previously created values, which could lead to a memory leak.

1 Like

Though MaybeUninit::uninit().assume_init() is always undefined behavior, isn't it? (Especially if you don't know anything about T, so can't even assume which bit patterns would be valid for it)

1 Like

Here it is used to essentially create [MaybeUninit<T>; N], and that is explicitly documented to be sound.

1 Like

…which is an unfortunate inconsistency in the interface; fortunately, there's an uninit_array() method being developed which resolves this discrepancy.

Missed that, thanks for clearing it up!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.