Union MaybeUninit
union MaybeUninit<T> { ... }
A wrapper type to construct uninitialized instances of T.
Initialization invariant
The compiler, in general, assumes that a variable is properly initialized according to the requirements of the variable's type. For example, a variable of reference type must be aligned and non-null. This is an invariant that must always be upheld, even in unsafe code. As a consequence, zero-initializing a variable of reference type causes instantaneous undefined behavior, no matter whether that reference ever gets used to access memory:
#
use ;
let x: &i32 = unsafe ; // undefined behavior! ⚠️
// The equivalent code with `MaybeUninit<&i32>`:
let x: &i32 = unsafe ; // undefined behavior! ⚠️
This is exploited by the compiler for various optimizations, such as eliding
run-time checks and optimizing enum layout.
Similarly, entirely uninitialized memory may have any content, while a bool must
always be true or false. Hence, creating an uninitialized bool is undefined behavior:
#
use ;
let b: bool = unsafe ; // undefined behavior! ⚠️
// The equivalent code with `MaybeUninit<bool>`:
let b: bool = unsafe ; // undefined behavior! ⚠️
Moreover, uninitialized memory is special in that it does not have a fixed value ("fixed" meaning "it won't change without being written to"). Reading the same uninitialized byte multiple times can give different results. This makes it undefined behavior to have uninitialized data in a variable even if that variable has an integer type, which otherwise can hold any fixed bit pattern:
#
use ;
let x: i32 = unsafe ; // undefined behavior! ⚠️
// The equivalent code with `MaybeUninit<i32>`:
let x: i32 = unsafe ; // undefined behavior! ⚠️
On top of that, remember that most types have additional invariants beyond merely
being considered initialized at the type level. For example, a 1-initialized Vec<T>
is considered initialized (under the current implementation; this does not constitute
a stable guarantee) because the only requirement the compiler knows about it
is that the data pointer must be non-null. Creating such a Vec<T> does not cause
immediate undefined behavior, but will cause undefined behavior with most
safe operations (including dropping it).
Examples
MaybeUninit<T> serves to enable unsafe code to deal with uninitialized data.
It is a signal to the compiler indicating that the data here might not
be initialized:
use MaybeUninit;
// Create an explicitly uninitialized reference. The compiler knows that data inside
// a `MaybeUninit<T>` may be invalid, and hence this is not UB:
let mut x = uninit;
// Set it to a valid value.
x.write;
// Extract the initialized data -- this is only allowed *after* properly
// initializing `x`!
let x = unsafe ;
The compiler then knows to not make any incorrect assumptions or optimizations on this code.
You can think of MaybeUninit<T> as being a bit like Option<T> but without
any of the run-time tracking and without any of the safety checks.
out-pointers
You can use MaybeUninit<T> to implement "out-pointers": instead of returning data
from a function, pass it a pointer to some (uninitialized) memory to put the
result into. This can be useful when it is important for the caller to control
how the memory the result is stored in gets allocated, and you want to avoid
unnecessary moves.
use MaybeUninit;
unsafe
let mut v = uninit;
unsafe
// Now we know `v` is initialized! This also makes sure the vector gets
// properly dropped.
let v = unsafe ;
assert_eq!;
Initializing an array element-by-element
MaybeUninit<T> can be used to initialize a large array element-by-element:
use ;
let data = ;
assert_eq!;
You can also work with partially initialized arrays, which could be found in low-level datastructures.
use MaybeUninit;
// Create an uninitialized array of `MaybeUninit`.
let mut data: = ;
// Count the number of elements we have assigned.
let mut data_len: usize = 0;
for elem in &mut data
// For each item in the array, drop if we allocated it.
for elem in &mut data
Initializing a struct field-by-field
You can use MaybeUninit<T> and the &raw mut syntax to initialize structs field by field:
use MaybeUninit;
let foo = ;
assert_eq!;
Layout
MaybeUninit<T> is guaranteed to have the same size, alignment, and ABI as T:
use MaybeUninit;
assert_eq!;
assert_eq!;
However remember that a type containing a MaybeUninit<T> is not necessarily the same
layout; Rust does not in general guarantee that the fields of a Foo<T> have the same order as
a Foo<U> even if T and U have the same size and alignment. Furthermore because any bit
value is valid for a MaybeUninit<T> the compiler can't apply non-zero/niche-filling
optimizations, potentially resulting in a larger size:
# use MaybeUninit;
assert_eq!;
assert_eq!;
If T is FFI-safe, then so is MaybeUninit<T>.
While MaybeUninit is #[repr(transparent)] (indicating it guarantees the same size,
alignment, and ABI as T), this does not change any of the previous caveats. Option<T> and
Option<MaybeUninit<T>> may still have different sizes, and types containing a field of type
T may be laid out (and sized) differently than if that field were MaybeUninit<T>.
MaybeUninit is a union type, and #[repr(transparent)] on unions is unstable (see the
tracking issue). Over time, the exact
guarantees of #[repr(transparent)] on unions may evolve, and MaybeUninit may or may not
remain #[repr(transparent)]. That said, MaybeUninit<T> will always guarantee that it has
the same size, alignment, and ABI as T; it's just that the way MaybeUninit implements that
guarantee may evolve.
Note that even though T and MaybeUninit<T> are ABI compatible it is still unsound to
transmute &mut T to &mut MaybeUninit<T> and expose that to safe code because it would allow
safe code to access uninitialized memory:
use MaybeUninit;
Validity
MaybeUninit<T> has no validity requirements – any sequence of bytes of
the appropriate length, initialized or uninitialized, are a valid
representation.
Moving or copying a value of type MaybeUninit<T> (i.e., performing a
"typed copy") will exactly preserve the contents, including the
provenance, of all non-padding bytes of type T in the value's
representation.
Therefore MaybeUninit can be used to perform a round trip of a value from
type T to type MaybeUninit<U> then back to type T, while preserving
the original value, if two conditions are met. One, type U must have the
same size as type T. Two, for all byte offsets where type U has padding,
the corresponding bytes in the representation of the value must be
uninitialized.
For example, due to the fact that the type [u8; size_of::<T>] has no
padding, the following is sound for any type T and will return the
original value:
# use ;
# ;
Note: Copying a value that contains references may implicitly reborrow them causing the provenance of the returned value to differ from that of the original. This applies equally to the trivial identity function:
Note: Moving or copying a value whose representation has initialized bytes
at byte offsets where the type has padding may lose the value of those
bytes, so while the original value will be preserved, the original
representation of that value as bytes may not be. Again, this applies
equally to trivial_identity.
Note: Performing this round trip when type U has padding at byte offsets
where the representation of the original value has initialized bytes may
produce undefined behavior or a different value. For example, the following
is unsound since T requires all bytes to be initialized:
# use ;
;
;
Conversely, the following is sound since T allows uninitialized bytes in
the representation of a value, but the round trip may alter the value:
# use ;
;
;
Implementations
impl<T> MaybeUninit<T>
const fn new(val: T) -> MaybeUninit<T>Creates a new
MaybeUninit<T>initialized with the given value. It is safe to callassume_initon the return value of this function.Note that dropping a
MaybeUninit<T>will never callT's drop code. It is your responsibility to make sureTgets dropped if it got initialized.Example
use MaybeUninit; let v: = new; # // Prevent leaks for Miri # unsafeconst fn uninit() -> MaybeUninit<T>Creates a new
MaybeUninit<T>in an uninitialized state.Note that dropping a
MaybeUninit<T>will never callT's drop code. It is your responsibility to make sureTgets dropped if it got initialized.See the [type-level documentation][MaybeUninit] for some examples.
Example
use MaybeUninit; let v: = uninit;const fn zeroed() -> MaybeUninit<T>Creates a new
MaybeUninit<T>in an uninitialized state, with the memory being filled with0bytes. It depends onTwhether that already makes for proper initialization. For example,MaybeUninit<usize>::zeroed()is initialized, butMaybeUninit<&'static i32>::zeroed()is not because references must not be null.Note that if
Thas padding bytes, those bytes are not preserved when theMaybeUninit<T>value is returned from this function, so those bytes will not be zeroed.Note that dropping a
MaybeUninit<T>will never callT's drop code. It is your responsibility to make sureTgets dropped if it got initialized.Example
Correct usage of this function: initializing a struct with zero, where all fields of the struct can hold the bit-pattern 0 as a valid value.
use MaybeUninit; let x = zeroed; let x = unsafe ; assert_eq!;This can be used in const contexts, such as to indicate the end of static arrays for plugin registration.
Incorrect usage of this function: calling
x.zeroed().assume_init()when0is not a valid bit-pattern for the type:use MaybeUninit; let x = zeroed; let x = unsafe ; // Inside a pair, we create a `NotZero` that does not have a valid discriminant. // This is undefined behavior. ⚠️const fn write(self: &mut Self, val: T) -> &mut TSets the value of the
MaybeUninit<T>.This overwrites any previous value without dropping it, so be careful not to use this twice unless you want to skip running the destructor. For your convenience, this also returns a mutable reference to the (now safely initialized) contents of
self.As the content is stored inside a
ManuallyDrop, the destructor is not run for the inner data if the MaybeUninit leaves scope without a call toassume_init,assume_init_drop, or similar. Code that receives the mutable reference returned by this function needs to keep this in mind. The safety model of Rust regards leaks as safe, but they are usually still undesirable. This being said, the mutable reference behaves like any other mutable reference would, so assigning a new value to it will drop the old content.Examples
Correct usage of this method:
use MaybeUninit; let mut x = uninit; // x is initialized now: let s = unsafe ; assert_eq!;This usage of the method causes a leak:
use MaybeUninit; let mut x = uninit; x.write; # // FIXME(https://github.com/rust-lang/miri/issues/3670): # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. # unsafe // This leaks the contained string: x.write; // x is initialized now: let s = unsafe ;This method can be used to avoid unsafe in some cases. The example below shows a part of an implementation of a fixed sized arena that lends out pinned references. With
write, we can avoid the need to write through a raw pointer:use Pin; use MaybeUninit;const fn as_ptr(self: &Self) -> *const TGets a pointer to the contained value. Reading from this pointer or turning it into a reference is undefined behavior unless the
MaybeUninit<T>is initialized. Writing to memory that this pointer (non-transitively) points to is undefined behavior (except inside anUnsafeCell<T>).Examples
Correct usage of this method:
use MaybeUninit; let mut x = uninit; x.write; // Create a reference into the `MaybeUninit<T>`. This is okay because we initialized it. let x_vec = unsafe ; assert_eq!; # // Prevent leaks for Miri # unsafeIncorrect usage of this method:
use MaybeUninit; let x = uninit; let x_vec = unsafe ; // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️(Notice that the rules around references to uninitialized data are not finalized yet, but until they are, it is advisable to avoid them.)
const fn as_mut_ptr(self: &mut Self) -> *mut TGets a mutable pointer to the contained value. Reading from this pointer or turning it into a reference is undefined behavior unless the
MaybeUninit<T>is initialized.Examples
Correct usage of this method:
use MaybeUninit; let mut x = uninit; x.write; // Create a reference into the `MaybeUninit<Vec<u32>>`. // This is okay because we initialized it. let x_vec = unsafe ; x_vec.push; assert_eq!; # // Prevent leaks for Miri # unsafeIncorrect usage of this method:
use MaybeUninit; let mut x = uninit; let x_vec = unsafe ; // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️(Notice that the rules around references to uninitialized data are not finalized yet, but until they are, it is advisable to avoid them.)
unsafe const fn assume_init(self: Self) -> TExtracts the value from the
MaybeUninit<T>container. This is a great way to ensure that the data will get dropped, because the resultingTis subject to the usual drop handling.Safety
It is up to the caller to guarantee that the
MaybeUninit<T>really is in an initialized state. Calling this when the content is not yet fully initialized causes immediate undefined behavior. The type-level documentation contains more information about this initialization invariant.On top of that, remember that most types have additional invariants beyond merely being considered initialized at the type level. For example, a
1-initializedVec<T>is considered initialized (under the current implementation; this does not constitute a stable guarantee) because the only requirement the compiler knows about it is that the data pointer must be non-null. Creating such aVec<T>does not cause immediate undefined behavior, but will cause undefined behavior with most safe operations (including dropping it).Examples
Correct usage of this method:
use MaybeUninit; let mut x = uninit; x.write; let x_init = unsafe ; assert_eq!;Incorrect usage of this method:
use MaybeUninit; let x = uninit; let x_init = unsafe ; // `x` had not been initialized yet, so this last line caused undefined behavior. ⚠️unsafe const fn assume_init_read(self: &Self) -> TReads the value from the
MaybeUninit<T>container. The resultingTis subject to the usual drop handling.Whenever possible, it is preferable to use
assume_initinstead, which prevents duplicating the content of theMaybeUninit<T>.Safety
It is up to the caller to guarantee that the
MaybeUninit<T>really is in an initialized state. Calling this when the content is not yet fully initialized causes undefined behavior. The type-level documentation contains more information about this initialization invariant.Moreover, similar to the
ptr::readfunction, this function creates a bitwise copy of the contents, regardless whether the contained type implements theCopytrait or not. When using multiple copies of the data (by callingassume_init_readmultiple times, or first callingassume_init_readand thenassume_init), it is your responsibility to ensure that data may indeed be duplicated.Examples
Correct usage of this method:
use MaybeUninit; let mut x = uninit; x.write; let x1 = unsafe ; // `u32` is `Copy`, so we may read multiple times. let x2 = unsafe ; assert_eq!; let mut x = uninit; x.write; let x1 = unsafe ; // Duplicating a `None` value is okay, so we may read multiple times. let x2 = unsafe ; assert_eq!;Incorrect usage of this method:
use MaybeUninit; let mut x = uninit; x.write; let x1 = unsafe ; let x2 = unsafe ; // We now created two copies of the same vector, leading to a double-free ⚠️ when // they both get dropped!unsafe const fn assume_init_drop(self: &mut Self) where T:Drops the contained value in place.
If you have ownership of the
MaybeUninit, you can also useassume_initas an alternative.Safety
It is up to the caller to guarantee that the
MaybeUninit<T>really is in an initialized state. Calling this when the content is not yet fully initialized causes undefined behavior.On top of that, all additional invariants of the type
Tmust be satisfied, as theDropimplementation ofT(or its members) may rely on this. For example, setting aVec<T>to an invalid but non-null address makes it initialized (under the current implementation; this does not constitute a stable guarantee), because the only requirement the compiler knows about it is that the data pointer must be non-null. Dropping such aVec<T>however will cause undefined behavior.unsafe const fn assume_init_ref(self: &Self) -> &TGets a shared reference to the contained value.
This can be useful when we want to access a
MaybeUninitthat has been initialized but don't have ownership of theMaybeUninit(preventing the use of.assume_init()).Safety
Calling this when the content is not yet fully initialized causes undefined behavior: it is up to the caller to guarantee that the
MaybeUninit<T>really is in an initialized state.Examples
Correct usage of this method:
use MaybeUninit; let mut x = uninit; # let mut x_mu = x; # let mut x = &mut x_mu; // Initialize `x`: x.write; // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to // create a shared reference to it: let x: & = unsafe ; assert_eq!; # // Prevent leaks for Miri # unsafeIncorrect usages of this method:
use MaybeUninit; let x = uninit; let x_vec: & = unsafe ; // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️use ; let b = uninit; // Initialize the `MaybeUninit` using `Cell::set`: unsafeunsafe const fn assume_init_mut(self: &mut Self) -> &mut TGets a mutable (unique) reference to the contained value.
This can be useful when we want to access a
MaybeUninitthat has been initialized but don't have ownership of theMaybeUninit(preventing the use of.assume_init()).Safety
Calling this when the content is not yet fully initialized causes undefined behavior: it is up to the caller to guarantee that the
MaybeUninit<T>really is in an initialized state. For instance,.assume_init_mut()cannot be used to initialize aMaybeUninit.Examples
Correct usage of this method:
# use MaybeUninit; # unsafe extern "C" # extern "C" let mut buf = uninit; // Initialize `buf`: unsafe // Now we know that `buf` has been initialized, so we could `.assume_init()` it. // However, using `.assume_init()` may trigger a `memcpy` of the 1024 bytes. // To assert our buffer has been initialized without copying it, we upgrade // the `&mut MaybeUninit<[u8; 1024]>` to a `&mut [u8; 1024]`: let buf: &mut = unsafe ; // Now we can use `buf` as a normal slice: buf.sort_unstable; assert!;Incorrect usages of this method:
You cannot use
.assume_init_mut()to initialize a value:use MaybeUninit; let mut b = uninit; unsafeFor instance, you cannot
Readinto an uninitialized buffer:use ;Nor can you use direct field access to do field-by-field gradual initialization:
use ; let foo: Foo = unsafe ;unsafe const fn array_assume_init<N: usize>(array: [Self; N]) -> [T; N]Extracts the values from an array of
MaybeUninitcontainers.Safety
It is up to the caller to guarantee that all elements of the array are in an initialized state.
Examples
use MaybeUninit; let mut array: = ; array.write; array.write; array.write; // SAFETY: Now safe as we initialised all elements let array = unsafe ; assert_eq!;const fn as_bytes(self: &Self) -> &[MaybeUninit<u8>]Returns the contents of this
MaybeUninitas a slice of potentially uninitialized bytes.Note that even if the contents of a
MaybeUninithave been initialized, the value may still contain padding bytes which are left uninitialized.Examples
use MaybeUninit; let val = 0x12345678_i32; let uninit = new; let uninit_bytes = uninit.as_bytes; let bytes = unsafe ; assert_eq!;const fn as_bytes_mut(self: &mut Self) -> &mut [MaybeUninit<u8>]Returns the contents of this
MaybeUninitas a mutable slice of potentially uninitialized bytes.Note that even if the contents of a
MaybeUninithave been initialized, the value may still contain padding bytes which are left uninitialized.Examples
use MaybeUninit; let val = 0x12345678_i32; let mut uninit = new; let uninit_bytes = uninit.as_bytes_mut; if cfg! else let val2 = unsafe ; assert_eq!;
impl<T, N: usize> MaybeUninit<[T; N]>
const fn transpose(self: Self) -> [MaybeUninit<T>; N]Transposes a
MaybeUninit<[T; N]>into a[MaybeUninit<T>; N].Examples
# use MaybeUninit; let data: = uninit.transpose;
impl<T> Any for MaybeUninit<T>
fn type_id(self: &Self) -> TypeId
impl<T> Borrow for MaybeUninit<T>
fn borrow(self: &Self) -> &T
impl<T> BorrowMut for MaybeUninit<T>
fn borrow_mut(self: &mut Self) -> &mut T
impl<T> CloneToUninit for MaybeUninit<T>
unsafe fn clone_to_uninit(self: &Self, dest: *mut u8)
impl<T> Debug for MaybeUninit<T>
fn fmt(self: &Self, f: &mut Formatter<'_>) -> Result
impl<T> Freeze for MaybeUninit<T>
impl<T> From for MaybeUninit<T>
fn from(t: T) -> TReturns the argument unchanged.
impl<T> RefUnwindSafe for MaybeUninit<T>
impl<T> Send for MaybeUninit<T>
impl<T> Sync for MaybeUninit<T>
impl<T> Unpin for MaybeUninit<T>
impl<T> UnsafeUnpin for MaybeUninit<T>
impl<T> UnwindSafe for MaybeUninit<T>
impl<T, N: usize> AsMut for MaybeUninit<[T; N]>
fn as_mut(self: &mut Self) -> &mut [MaybeUninit<T>; N]
impl<T, N: usize> AsMut for MaybeUninit<[T; N]>
fn as_mut(self: &mut Self) -> &mut [MaybeUninit<T>]
impl<T, N: usize> AsRef for MaybeUninit<[T; N]>
fn as_ref(self: &Self) -> &[MaybeUninit<T>; N]
impl<T, N: usize> AsRef for MaybeUninit<[T; N]>
fn as_ref(self: &Self) -> &[MaybeUninit<T>]
impl<T, N: usize> From for MaybeUninit<[T; N]>
fn from(arr: [MaybeUninit<T>; N]) -> Self
impl<T, U> Into for MaybeUninit<T>
fn into(self: Self) -> UCalls
U::from(self).That is, this conversion is whatever the implementation of
[From]<T> for Uchooses to do.
impl<T, U> TryFrom for MaybeUninit<T>
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
impl<T, U> TryInto for MaybeUninit<T>
fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>
impl<T: $crate::marker::Copy> Copy for MaybeUninit<T>
impl<T: Copy> Clone for MaybeUninit<T>
fn clone(self: &Self) -> Self