How can/should I obtain a &[MaybeUninit<T>]
from a &[T]
?
Intuitively, I should be allowed to treat initialized memory as being possibly uninitialized, but intuition and unsafe
do not always mix well... Since MaybeUninit<T>
is guaranteed to have the same size and alignment as T
, it should be safe to transmute
a &[T]
into a &[MaybeUninit<T>]
?
Then again, the current implementation of MaybeUninit::new(val)
does more than just transmuting, it adds a ManuallyDrop::new(val)
wrapper. Does this mean that if I implemented slice_as_maybeuninit
via transmuting, I'd run into problems with destructors not being run? Which is not unsafe
, but still undesirable enough to restrict such a function to slices of data that implement Copy
(i.e., which do not implement Drop
).
To make my question(s) more precise:
fn slice_as_maybeuninit<'a, T>(s: &'a [T]) -> &'a [MaybeUninit<T>]
, if at all?fn slice_as_maybeuninit<'a, T: Copy>(s: &'a [T]) -> &'a [MaybeUninit<T>]
? // note the T: Copy
boundstd::mem::MaybeUninit
not provide these operations for me?How should I implement
fn slice_as_maybeuninit<'a, T>(s: &'a [T]) -> &'a [MaybeUninit<T>]
, if at all?
Not using transmute (I'm unsure if this is unsound because layout of slices is not guaranteed, but it's definitely not good practice). However, a simple cast will do:
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
can also help here, if you can implement the NoUninit
and AnyBitPattern
traits for T
. Just use must_cast_slice()
.
An important note, however, is that it is not sound to implement &mut [T]
-> &mut [MaybeUninit<T>]
(or &UnsafeCell<[T]>
-> &UnsafeCell<[MaybeUninit<T>]>
, or with any other interior mutability container) conversion, since that will allow the user to perform the conversion, put MaybeUninit::uninit()
here, then read it as initialized from the original reference.
How should I implement
fn slice_as_maybeuninit<'a, T: Copy>(s: &'a [T]) -> &'a [MaybeUninit<T>]
? // note theT: Copy
bound
By calling into the previous function, I guess?
Bonus: Why does
std::mem::MaybeUninit
not provide these operations for me?
It doesn't provide a lot of possible operations. You can suggest it, or maybe Project Safe Transmute will eventually make this possible.