преобразование `MaybeUninit` в `T`, но получение ошибки E0382

#rust

#Ржавчина

Вопрос:

Я пытаюсь воспроизвести код, предложенный в MaybeUninit документах. В частности, похоже, что он работает с определенными типами данных, но выдает ошибку компилятора для универсальных типов.

Рабочий пример (с 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 amp;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!(amp;data[0], amp;42);
}
 

Неудачный пример (с общим 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 amp;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!(amp;data[0], amp;42);
}
 

ошибка:

    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
 

Ссылка на игровую площадку здесь

Вопросы

  1. почему второй пример терпит неудачу? (Я думал MaybeUninit<T> , что всегда можно преобразовать в a T , потому что они гарантированно будут иметь одинаковую компоновку памяти.)
  2. можно ли переписать пример для работы с универсальными типами?

Ответ №1:

Это известная проблема (связанная), вы можете исправить код, используя подсказки HadrienG2, выполнив более небезопасную небезопасную вещь:

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

В будущем мы ожидаем, что сможем использовать array_assume_init() .