Search code examples
rustinitializationmaybeuninit

What is the difference between `MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init()` and `[MaybeUninit<T>::uninit(); N]`?


The Rust documentation for MaybeUninit explain that an array should be initialized with...

let mut array: [MaybeUninit<T>; N] = unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() };

However, could I not just initialize the array with...

let mut array: [MaybeUninit<T>; N] = [MaybeUninit<T>::uninit(); N];

I created a reproduction in Godbolt and I could not see any difference between these two examples.

I did find this GitHub issue which appears to imply there may have been a difference between these two examples in the past. However, it appears that this has now been fixed?

So, is there a difference between these two examples? Why does the documentation recommend the first, when the second is more concise and easier to understand? Should the documentation be updated?


Solution

  • So... I wrote this question and just before I published it, I figured it out!

    When T does implement Copy, then [MaybeUninit<T>::uninit(); N] is valid, as impl<T> Copy for MaybeUninit<T> where T: Copy. However, when T does not implement Copy, then it does not work. In that case, you are required to use unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() } instead.

    From the Rust documentation for an array...

    A repeat expression [expr; N] where N is how many times to repeat expr in the array. expr must either be:

    • A value of a type implementing the Copy trait,
    • A const value.

    However, this also explains why in the documentation for MaybeUninit::uninit_array it mentions that...

    Note: in a future Rust version this method may become unnecessary when Rust allows inline const expressions. The example below could then use let mut buf = [const { MaybeUninit::<u8>::uninit() }; 32];.