Skip to content

Commit c5e4a5f

Browse files
authored
Rollup merge of #140463 - joshlf:patch-13, r=RalfJung
Document MaybeUninit bit validity Partially addresses rust-lang/unsafe-code-guidelines#555 by clarifying that it is sound to write any byte values (initialized or uninitialized) to any `MaybeUninit<T>` regardless of `T`. r? `@RalfJung`
2 parents 4b3ba58 + 11627f0 commit c5e4a5f

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

library/core/src/mem/maybe_uninit.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,89 @@ use crate::{fmt, intrinsics, ptr, slice};
253253
/// std::process::exit(*code); // UB! Accessing uninitialized memory.
254254
/// }
255255
/// ```
256+
///
257+
/// # Validity
258+
///
259+
/// `MaybeUninit<T>` has no validity requirements –- any sequence of [bytes] of
260+
/// the appropriate length, initialized or uninitialized, are a valid
261+
/// representation.
262+
///
263+
/// Moving or copying a value of type `MaybeUninit<T>` (i.e., performing a
264+
/// "typed copy") will exactly preserve the contents, including the
265+
/// [provenance], of all non-padding bytes of type `T` in the value's
266+
/// representation.
267+
///
268+
/// Therefore `MaybeUninit` can be used to perform a round trip of a value from
269+
/// type `T` to type `MaybeUninit<U>` then back to type `T`, while preserving
270+
/// the original value, if two conditions are met. One, type `U` must have the
271+
/// same size as type `T`. Two, for all byte offsets where type `U` has padding,
272+
/// the corresponding bytes in the representation of the value must be
273+
/// uninitialized.
274+
///
275+
/// For example, due to the fact that the type `[u8; size_of::<T>]` has no
276+
/// padding, the following is sound for any type `T` and will return the
277+
/// original value:
278+
///
279+
/// ```rust,no_run
280+
/// # use core::mem::{MaybeUninit, transmute};
281+
/// # struct T;
282+
/// fn identity(t: T) -> T {
283+
/// unsafe {
284+
/// let u: MaybeUninit<[u8; size_of::<T>()]> = transmute(t);
285+
/// transmute(u) // OK.
286+
/// }
287+
/// }
288+
/// ```
289+
///
290+
/// Note: Copying a value that contains references may implicitly reborrow them
291+
/// causing the provenance of the returned value to differ from that of the
292+
/// original. This applies equally to the trivial identity function:
293+
///
294+
/// ```rust,no_run
295+
/// fn trivial_identity<T>(t: T) -> T { t }
296+
/// ```
297+
///
298+
/// Note: Moving or copying a value whose representation has initialized bytes
299+
/// at byte offsets where the type has padding may lose the value of those
300+
/// bytes, so while the original value will be preserved, the original
301+
/// *representation* of that value as bytes may not be. Again, this applies
302+
/// equally to `trivial_identity`.
303+
///
304+
/// Note: Performing this round trip when type `U` has padding at byte offsets
305+
/// where the representation of the original value has initialized bytes may
306+
/// produce undefined behavior or a different value. For example, the following
307+
/// is unsound since `T` requires all bytes to be initialized:
308+
///
309+
/// ```rust,no_run
310+
/// # use core::mem::{MaybeUninit, transmute};
311+
/// #[repr(C)] struct T([u8; 4]);
312+
/// #[repr(C)] struct U(u8, u16);
313+
/// fn unsound_identity(t: T) -> T {
314+
/// unsafe {
315+
/// let u: MaybeUninit<U> = transmute(t);
316+
/// transmute(u) // UB.
317+
/// }
318+
/// }
319+
/// ```
320+
///
321+
/// Conversely, the following is sound since `T` allows uninitialized bytes in
322+
/// the representation of a value, but the round trip may alter the value:
323+
///
324+
/// ```rust,no_run
325+
/// # use core::mem::{MaybeUninit, transmute};
326+
/// #[repr(C)] struct T(MaybeUninit<[u8; 4]>);
327+
/// #[repr(C)] struct U(u8, u16);
328+
/// fn non_identity(t: T) -> T {
329+
/// unsafe {
330+
/// // May lose an initialized byte.
331+
/// let u: MaybeUninit<U> = transmute(t);
332+
/// transmute(u)
333+
/// }
334+
/// }
335+
/// ```
336+
///
337+
/// [bytes]: ../../reference/memory-model.html#bytes
338+
/// [provenance]: crate::ptr#provenance
256339
#[stable(feature = "maybe_uninit", since = "1.36.0")]
257340
// Lang item so we can wrap other types in it. This is useful for coroutines.
258341
#[lang = "maybe_uninit"]

0 commit comments

Comments
 (0)