0

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?

scottwillmoore
  • 450
  • 2
  • 9

1 Answers1

2

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];.

scottwillmoore
  • 450
  • 2
  • 9
  • 1
    As a note: If you are looking to avoid `unsafe` then I would recommend the use of `std::array::from_fn`, which is pretty concise. And since `MaybeUninit::uninit` is `const` and doesn't do anything really, I would be a bit surprised if the compiler couldn't evaluate `from_fn` at compile-time. – PatientPenguin Jun 17 '23 at 08:02
  • @PatientPenguin: The compiler can only evaluate `const fn`s at compile-time, and `std::array::from_fn` is not one. – eggyal Jun 17 '23 at 09:03
  • 2
    I might be mistaken, but I was under the impression that `const` guarantees that a function is evaluated at compile time, though it is not a requirement for it. The compiler is free to, if it determines it is possible, evaluate a non-const function at compile time. – PatientPenguin Jun 17 '23 at 09:59
  • 1
    @PatientPenguin You _are_ mistaken: you are correct that being `const` is not a requirement for evaluating at compile-time, but you're wrong that `const` guarantees compile time evaluation. It does not. Moreover, most of the times it will never do that, almost guaranteed _not_ to do that. It is like `constexpr` in C++, not like `consteval`. – Chayim Friedman Jun 17 '23 at 19:26
  • 1
    @PatientPenguin But since the very motivation for using `MaybeUninit` is performance (if you don't care about it you can just use the safe `Option`), I would not suggest `array::from_fn()` which tends to optimize poorly with checking that it optimizes well. Though, it looks like it does. – Chayim Friedman Jun 17 '23 at 19:27
  • Huh. Thank you for the correction and information @ChayimFriedman! – PatientPenguin Jun 17 '23 at 23:57