Currently, uninitialized memory is dealt with using a MaybeUninit<T> type. But:
- Unitialized memory is difficult
- It can also store inproperly initialized values
So it might be better to dedicate MaybeUninit to the first point only and separate the second into a distinct type.
Idea
Create a new type that can store T without proper initialization, but unlike MaybeUninit it must contain a fixed value:
| Valid value | Invalid value | Non-fixed value | |
|---|---|---|---|
MaybeUninit<T> |
|||
MaybeInvalid<T> |
|||
T |
#[repr(transparent)]
pub union MaybeInvalid<T> {
invalid: (),
value: ManuallyDrop<T>,
}
impl<T> MaybeInvalid<T> {
...
// similar to MaybeUninit<T>
}
MaybeUninit would get new functions to interact with it:
impl<T> MaybeUninit<T> {
...
pub fn written(MaybeInvalid<T>) -> Self;
pub unsafe fn assume_written(self) -> MaybeInvalid<T>;
pub unsafe fn assume_written_ref(&self) -> &MaybeInvalid<T>;
pub unsafe fn assume_written_mut(&mut self) -> &mut MaybeInvalid<T>;
pub fn write_raw(&mut self, MaybeInvalid<T>) -> &mut MaybeInvalid<T>;
}
Case 1: enum optimization
We could define a trait that gives a compiler "an example" of invalid value of this type:
/// ## Safety
/// INVALID's value must be actually invalid:
/// impossible to obtain in safe Rust
pub unsafe trait Invalid: Sized {
const INVALID: MaybeInvalid<Self>;
}
// example
unsafe impl<'a, T> Invalid for &'a T {
const INVALID: MaybeInvalid<Self> = MaybeInvalid::zeroed();
}
This could be used to optimize enums layout for arbitrary user types:
for<T: Invalid> assert_eq!(size_of::<T>(), size_of::<Option<T>>)
Case 2: freezeing uninitialized values
LLVM's freeze instruction, mentioned a couple of times (17188, 22254, 13231 from a quick search) here, would become trivial and safe:
impl<T> MaybeUninit<T> {
...
pub fn freeze(self) -> MaybeInvalid<T>;
}
And if we had some auto trait for always-valid types, then one could create an "uninitialized" buffer without using unsafe:
let mut buf = MaybeUninit::<[u8; 256]>::uninit().freeze().valid();
That trait would be mutually exclusive with Invalid from case 1 though, which is probably fine - there are Drop and Copy but these are fundamental.