Безопасное обращение с `&[T]` как с `&[MaybeUninit<T>]` в Rust

Как я могу/должен получить &[MaybeUninit<T>] от &[T]?

Интуитивно мне должно быть разрешено рассматривать инициализированную память как возможно неинициализированную, но интуиция и unsafe не всегда хорошо сочетаются... Поскольку MaybeUninit<T> гарантированно будет иметь тот же размер и выравнивание, что и T, должно быть безопасно вставлять transmute a &[T] в а &[MaybeUninit<T>]?

Опять же, текущая реализация MaybeUninit::new(val) делает больше, чем просто трансмутацию, она добавляет обертку ManuallyDrop::new(val). Означает ли это, что если бы я реализовал slice_as_maybeuninit посредством трансмутации, я столкнулся бы с проблемами, связанными с тем, что деструкторы не запускаются? Это не unsafe, но все же достаточно нежелательно, чтобы ограничивать такую ​​функцию фрагментами данных, которые реализуют Copy (т. е. которые не реализуют Drop).

Чтобы сделать мой вопрос(ы) более точным:

  • Как мне реализовать fn slice_as_maybeuninit<'a, T>(s: &'a [T]) -> &'a [MaybeUninit<T>], если вообще?
  • Как мне реализовать fn slice_as_maybeuninit<'a, T: Copy>(s: &'a [T]) -> &'a [MaybeUninit<T>]? // обратите внимание на границу T: Copy
  • Бонус: Почему std::mem::MaybeUninit не предоставляет мне эти операции?

Это гипотетический вопрос, или у вас действительно есть законные основания забывать, что инициализированная память инициализируется?

BallpointBen 02.07.2024 23:08

@BallpointBen Вариант использования работает с функцией, которая работает с фрагментом элементов MaybeUninitialized. Я не могу изменить эту функцию, но мне нужна ее функциональность для «нормального» фрагмента данных. Являются ли варианты ввода этой функции ошибочными? Может быть. Изменит ли ошибочность вопрос на гипотетический? К сожалению нет.

Aljoscha Meyer 02.07.2024 23:13

Чтобы ответить на ваш бонусный вопрос. Многие API, связанные с неинициализированной памятью, в настоящее время нестабильны. Вполне возможно, что такое преобразование будет добавлено в будущем, хотя стабилизация текущих API, вероятно, будет иметь более высокий приоритет.

Aleksander Krauze 03.07.2024 07:46
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
3
3
102
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Как мне реализовать fn slice_as_maybeuninit<'a, T>(s: &'a [T]) -> &'a [MaybeUninit<T>], если вообще?

Не использовать трансмутацию (я не уверен, что это неправильно, поскольку расположение срезов не гарантируется, но это определенно не очень хорошая практика). Однако простой приведение подойдет:

fn slice_as_maybeuninit<'a, T>(s: &'a [T]) -> &'a [MaybeUninit<T>] {
    // SAFETY:
    //  - `MaybeUninit<T>` is guaranteed to have the same layout as `T`.
    //  - Slices with compatible elements layout have compatible layout,
    //    since slices are have the same layout as the backing array
    //    and array lay all elements consecutively.
    //  - It is always safe to treat initialized values as possibly-initialized.
    unsafe { &*(s as *const [T] as *const [MaybeUninit<T>]) }
}

bytemuck также может здесь помочь, если вы сможете реализовать черты NoUninit и AnyBitPattern для T. Просто используйте must_cast_slice().

Однако важно отметить, что нецелесообразно реализовывать преобразование &mut [T] -> &mut [MaybeUninit<T>] (или &UnsafeCell<[T]> -> &UnsafeCell<[MaybeUninit<T>]> или с любым другим внутренним контейнером изменяемости), поскольку это позволит пользователю выполнить преобразование, поместите MaybeUninit::uninit() здесь, затем прочитайте его как инициализированный из исходной ссылки.

Как мне реализовать fn slice_as_maybeuninit<'a, T: Copy>(s: &'a [T]) -> &'a [MaybeUninit<T>]? // обратите внимание на границу T: Copy

Я думаю, вызвав предыдущую функцию?

Бонус: Почему std::mem::MaybeUninit не предоставляет мне эти операции?

Он не предоставляет много возможных операций. Вы можете предложить это, или, может быть, Проект «Безопасное трансмутирование» в конечном итоге сделает это возможным.

Трансмутация из [T] в [MaybeUninit<T>] совершенно безопасна. Документы по его макету:

MaybeUninit<T> гарантированно будет иметь тот же размер, выравнивание и ABI, что и T:

Однако вы хотите использовать from_raw_parts для создания нового среза вместо просто transmute.

fn slice_as_maybeuninit<T>(s: &[T]) -> &[MaybeUninit<T>] {
    unsafe { std::slice::from_raw_parts(s.as_ptr() as *const MaybeUninit<T>, s.len()) }
}

[Смогу ли я] столкнуться с проблемами из-за того, что деструкторы не запускаются?

Нет. В других обстоятельствах вы могли бы MaybeUninit ослабить критерии инициализации, чтобы он не удалял свое содержимое, поскольку сам по себе не знает, инициализировано ли его содержимое. Однако вы преобразуете только &[T], который является лишь ссылкой на содержимое; поэтому он все равно не будет пытаться удалить значения.

Copy не требуется, поскольку значения T не создаются.

Другие вопросы по теме