Как я могу/должен получить &[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 Вариант использования работает с функцией, которая работает с фрагментом элементов MaybeUninitialized. Я не могу изменить эту функцию, но мне нужна ее функциональность для «нормального» фрагмента данных. Являются ли варианты ввода этой функции ошибочными? Может быть. Изменит ли ошибочность вопрос на гипотетический? К сожалению нет.
Чтобы ответить на ваш бонусный вопрос. Многие API, связанные с неинициализированной памятью, в настоящее время нестабильны. Вполне возможно, что такое преобразование будет добавлено в будущем, хотя стабилизация текущих API, вероятно, будет иметь более высокий приоритет.
Как мне реализовать
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
не создаются.
Это гипотетический вопрос, или у вас действительно есть законные основания забывать, что инициализированная память инициализируется?