CrepeGoat
CrepeGoat

Reputation: 2515

converting `MaybeUninit<T>` to `T`, but getting error E0382

I'm trying to reproduce the code suggested in the MaybeUninit docs. Specifically, it seems to work with specific datatypes, but produces a compiler error on generic types.

Working example (with u32)

use std::mem::{self, MaybeUninit};

fn init_array(t: u32) -> [u32; 1000] {
    // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
    // safe because the type we are claiming to have initialized here is a
    // bunch of `MaybeUninit`s, which do not require initialization.
    let mut data: [MaybeUninit<u32>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };

    // Dropping a `MaybeUninit` does nothing. Thus using raw pointer
    // assignment instead of `ptr::write` does not cause the old
    // uninitialized value to be dropped. Also if there is a panic during
    // this loop, we have a memory leak, but there is no memory safety
    // issue.
    for elem in &mut data[..] {
        elem.write(t);
    }

    // Everything is initialized. Transmute the array to the
    // initialized type.
    unsafe { mem::transmute::<_, [u32; 1000]>(data) }
}

fn main() {
    let data = init_array(42);
    assert_eq!(&data[0], &42);
}

Failing example (with generic T)

use std::mem::{self, MaybeUninit};

fn init_array<T: Copy>(t: T) -> [T; 1000] {
    // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
    // safe because the type we are claiming to have initialized here is a
    // bunch of `MaybeUninit`s, which do not require initialization.
    let mut data: [MaybeUninit<T>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };

    // Dropping a `MaybeUninit` does nothing. Thus using raw pointer
    // assignment instead of `ptr::write` does not cause the old
    // uninitialized value to be dropped. Also if there is a panic during
    // this loop, we have a memory leak, but there is no memory safety
    // issue.
    for elem in &mut data[..] {
        elem.write(t);
    }

    // Everything is initialized. Transmute the array to the
    // initialized type.
    unsafe { mem::transmute::<_, [T; 1000]>(data) }
}

fn main() {
    let data = init_array(42);
    assert_eq!(&data[0], &42);
}

error:

   Compiling playground v0.0.1 (/playground)
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
  --> src/main.rs:20:14
   |
20 |     unsafe { mem::transmute::<_, [T; 1000]>(data) }
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: source type: `[MaybeUninit<T>; 1000]` (size can vary because of T)
   = note: target type: `[T; 1000]` (size can vary because of T)

For more information about this error, try `rustc --explain E0512`.
error: could not compile `playground` due to previous error

Playground link here

Questions

  1. why is the second example failing? (I thought MaybeUninit<T> could always be transmuted into a T because they'd be guaranteed to have the same memory layout.)
  2. can the example be rewritten to work with generic types?

Upvotes: 1

Views: 120

Answers (1)

Stargateur
Stargateur

Reputation: 26757

This is a known issue (related), you can fix the code using the tips of HadrienG2 by doing a more unsafe unsafe thing:

// Everything is initialized. Transmute the array to the
// initialized type.
let ptr = &mut data as *mut _ as *mut [T; 1000];
let res = unsafe { ptr.read() };
core::mem::forget(data);
res

In future we expect to be able to use array_assume_init().

Upvotes: 2

Related Questions