Это звук для трансмутации MaybeUninit <[T; N]> до [MaybeUninit <T>; N]? - PullRequest
7 голосов
/ 23 марта 2019

Звучит ли следующий код?

#![feature(maybe_uninit)]
use std::mem;
const N: usize = 2; // or another number
type T = String; // or any other type

fn main() {
    unsafe {
        // create an uninitialized array
        let t: mem::MaybeUninit<[T; N]> = mem::MaybeUninit::uninitialized();
        // convert it to an array of uninitialized values
        let mut t: [mem::MaybeUninit<T>; N] = mem::transmute(t);
        // initialize the values
        t[0].set("Hi".to_string());
        t[1].set("there".to_string());
        // use the values
        println!("{} {}", t[0].get_ref(), t[1].get_ref());
        // drop the values
        mem::replace(&mut t[0], mem::MaybeUninit::uninitialized()).into_initialized();
        mem::replace(&mut t[1], mem::MaybeUninit::uninitialized()).into_initialized();
    }
}

Должен заметить, что miri запускает его без проблем.

1 Ответ

5 голосов
/ 23 марта 2019

Исправление: Ответ ниже по-прежнему в общем случае, но в случае MaybeUninit есть несколько удобных особых случаев с разметкой памяти, которые делают это действительно безопасным:

Во-первых, документация для MaybeUninit имеет раздел макет , в котором говорится, что

MaybeUninit<T> гарантированно имеет тот же размер и выравнивание, что и T.

Во-вторых, в справочнике по языку говорится об разметках массивов :

Массивы расположены так, что элемент nth массива смещенот начала массива на n * the size of the type байта.Массив [T; n] имеет размер size_of::<T>() * n и такое же выравнивание T.

Это означает, что макет MaybeUninit<[T; n]> и макет [MaybeUninit<T>; n] совпадают.


Оригинальный ответ:

Из того, что я могу сказать, это одна из тех вещей, которые могут работать, но не гарантируются, и может зависеть от поведения компилятора или платформы.

MaybeUninit определяется следующим образом в источнике тока :

#[allow(missing_debug_implementations)]
#[unstable(feature = "maybe_uninit", issue = "53491")]
pub union MaybeUninit<T> {
    uninit: (),
    value: ManuallyDrop<T>,
}

Поскольку он не помечен атрибутом #[repr] (в отличие от, например, ManuallyDrop), он находится в представлении по умолчанию, о котором ссылка говорит это :

Номинальные типы без атрибута repr имеют представление по умолчанию.Неофициально это представление также называется представлением ржавчины.

Нет никаких гарантий размещения данных, сделанных этим представлением.

Для преобразования из *От 1067 * до [Wrapper<T>], это должен быть случай, когда расположение памяти Wrapper<T> равно точно так же, как расположение памяти T.Это относится ко многим оберткам, таким как ранее упомянутый ManuallyDrop, и они обычно помечаются атрибутом #[repr(transparent)].

Но в этом случае это не обязательно верно.Поскольку () является типом нулевого размера, вполне вероятно, что компилятор будет использовать ту же схему памяти для T и MaybeUninit<T> (и именно поэтому он работает для вас), но также возможно, что компилятор решитиспользовать какую-то другую структуру памяти (например, в целях оптимизации), в этом случае преобразование больше не будет работать.


В качестве конкретного примера, компилятор может использовать следующую схему памяти для MaybeUninit<T>:

+---+---+...+---+
| T         | b |     where b is "is initialized" flag
+---+---+...+---+

Согласно приведенной выше цитате, компилятору разрешено это делать.В этом случае [MaybeUninit<T>] и MaybeUninit<[T]> имеют разные макеты памяти, поскольку MaybeUninit<[T]> имеет один b для всего массива, в то время как [MaybeUninit<T>] имеет один b для каждого MaybeUninit<T> в массиве:

MaybeUninit<[T]>:
+---+...+---+---+...+---+...+---+...+---+---+
| T[0]      | T[1]      | … | T[n-1]    | b |
+---+...+---+---+...+---+...+---+...+---+---+
Total size: n * size_of::<T>() + 1

[MaybeUninit<T>]
+---+...+---+----+---+...+---+----+...+---+...+---+------+
| T[0]      |b[0]| T[1]      |b[1]| … | T[n-1]    |b[n-1]|
+---+...+---+----+---+...+---+----+...+---+...+---+------+
Total size: (n + 1) * size_of::<T>()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...