Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,89 @@ use crate::{fmt, intrinsics, ptr, slice};
/// std::process::exit(*code); // UB! Accessing uninitialized memory.
/// }
/// ```
///
/// # 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:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// # struct T;
/// fn identity(t: T) -> T {
/// unsafe {
/// let u: MaybeUninit<[u8; size_of::<T>()]> = transmute(t);
/// transmute(u) // OK.
/// }
/// }
/// ```
///
/// 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:
///
/// ```rust,no_run
/// fn trivial_identity<T>(t: T) -> T { t }
/// ```
///
/// 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:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// #[repr(C)] struct T([u8; 4]);
/// #[repr(C)] struct U(u8, u16);
/// fn unsound_identity(t: T) -> T {
/// unsafe {
/// let u: MaybeUninit<U> = transmute(t);
/// transmute(u) // UB.
/// }
/// }
/// ```
///
/// Conversely, the following is sound since `T` allows uninitialized bytes in
/// the representation of a value, but the round trip may alter the value:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// #[repr(C)] struct T(MaybeUninit<[u8; 4]>);
/// #[repr(C)] struct U(u8, u16);
/// fn non_identity(t: T) -> T {
/// unsafe {
/// // May lose an initialized byte.
/// let u: MaybeUninit<U> = transmute(t);
/// transmute(u)
/// }
/// }
/// ```
///
/// [bytes]: ../../reference/memory-model.html#bytes
/// [provenance]: crate::ptr#provenance
#[stable(feature = "maybe_uninit", since = "1.36.0")]
// Lang item so we can wrap other types in it. This is useful for coroutines.
#[lang = "maybe_uninit"]
Expand Down
Loading