Description
Goals
Many of the new MaybeUninit APIs deal with slices or arrays. The current state of affairs is unfortunate because it leads to API duplication. An API may need to be duplicated 3 times: once on MaybeUninit and then another two times for slices and arrays. In an ideal world, the type system could express some sort of container API where if something contains a MaybeUninit then certain methods are available. Since we don't live in that world, the primary goal is to make as many MaybeUninit methods available as possible on slices and arrays without duplication.
Other goals include guiding users towards the right APIs and making unsafely more visible.
Problematic APIs
MaybeUninit::{uninit_array,array_assume_init}
These APIs let you create MaybeUninit arrays, but do not let you manipulate them. Any APIs on MaybeUninit would need to be duplicated on arrays or accessed via slices (requiring borrowing).
Proposal
An API that converts between [MaybeUninit<T>; N]
<-> MaybeUninit<[T; N]>
. Since you can get a MaybeUninit
with your array inside it, anything on MaybeUninit will work for arrays.
Currently it is named transpose
but there may be clearer names.
Alternatives
Implement index traits on MaybeUninit<[T; N]>
My two primary concerns are that this prevents the natural use of array initialization syntax and would require bounds checking when that may not be desirable.
Furthermore, I don't think this works with MaybeUninits of non-Copy types unless you call the index method directly?
slice_assume_init
variants
Assuming a transpose-like API is not possible for slices, these methods need to be available.
Proposal
Currently they aren't inherent methods but should be.
as_bytes
variants
Based on the discussion below we should keep these methods to circumvent unsafety around invalid pointers.
Proposal
Currently they aren't inherent methods but should be. Add documentation warning users to be extra careful about meeting the invariants of the type they are mucking around with.
slice_as_ptr
variants
These methods are questionable because they combine the removal of bounds checking with raw pointer conversion. You may want one but not the other, and in either case, you should be required to use separate unsafes.
Proposal
Get rid of them and instead provide safe MaybeUninit pointer conversion to its underlying type (T). That is, *{const,mut} MaybeUninit<T> -> *{const,mut} T
.
write_slice
variants
Based on discussion in the tracking issue, the name of these methods has not conveyed the fact that write_cloned does not drop old items. I believe we should just use consistent names with slices and document this behavior in bold or something. And at worst, memory leaks are not unsafe in rust so eh.
Proposal
- Rename to
{copy,clone}_from_slice
- Move to inherent methods
uninit
This method cannot be used directly in array initializers if T is not Copy. However, using a const gets around this.
Proposal
Add an UNINIT const that does the same thing as uninit
.
Drawbacks
API duplication. This also isn't necessary with transpose, though it would look prettier.
API diff
impl<T> MaybeUninit<T> {
// Removed method. Can be replaced with `array.transpose().assume_init()`
- pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N]
// Removed method. Can be replaced with `MaybeUninit::<[T; N]>::uninit().transpose()`
- pub fn uninit_array<const N: usize>() -> [MaybeUninit<T>; N]
// Removed methods. These can be replaced with `slice.as_{,_mut}ptr().inner()`.
- pub fn slice_as_mut_ptr(this: &mut [MaybeUninit<T>]) -> *mut T
- pub fn slice_as_ptr(this: &[MaybeUninit<T>]) -> *const T
// Moved methods.
- pub unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T]
- pub unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T]
- pub fn write_slice<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]where T: Copy
- pub fn write_slice_cloned<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T] where T: Clone,
- pub fn slice_as_bytes(this: &[MaybeUninit<T>]) -> &[MaybeUninit<u8>]
- pub fn slice_as_bytes_mut(this: &mut [MaybeUninit<T>]) -> &mut [MaybeUninit<u8>]
}
// Moved slice methods
impl<T> [MaybeUninit<T>] {
+ pub unsafe fn assume_init_ref(&self) -> &[T]
+ pub unsafe fn assume_init_mut(&mut self) -> &mut [T]
+ pub fn copy_from_slice(&mut self, src: &[T]) -> &mut [T] where T: Copy
+ pub fn clone_from_slice(&mut self, src: &[T]) -> &mut [T] where T: Clone
+ pub fn as_bytes(&self) -> &[MaybeUninit<u8>]
+ pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>]
}
// Completely new methods
impl<T, const N: usize> MaybeUninit<[T; N]> {
+ pub fn transpose(self) -> [MaybeUninit<T>; N]
}
impl<T, const N: usize> [MaybeUninit<T>; N] {
+ pub fn transpose(self) -> MaybeUninit<[T; N]>;
}
impl<T> *const MaybeUninit<T> {
+ pub const fn inner(self) -> *const T
}
impl<T> *mut MaybeUninit<T> {
+ pub const fn inner(self) -> *mut T
}
Activity