Skip to content

Commit 7971b2a

Browse files
joshlfRalfJung
authored andcommitted
Document MaybeUninit bit validity
Co-authored-by: Ralf Jung <[email protected]> Edited-by: TC
1 parent dd7fda5 commit 7971b2a

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

library/core/src/mem/maybe_uninit.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,54 @@ use crate::{fmt, intrinsics, ptr, slice};
252252
/// std::process::exit(*code); // UB! Accessing uninitialized memory.
253253
/// }
254254
/// ```
255+
///
256+
/// # Validity
257+
///
258+
/// A `MaybeUninit<T>` has no validity requirement – any sequence of
259+
/// [bytes][reference-byte] of the appropriate length, initialized or
260+
/// uninitialized, are a valid representation of `MaybeUninit<T>`.
261+
///
262+
/// However, "round-tripping" via `MaybeUninit` does not always result in the
263+
/// original value. `MaybeUninit` can have padding, and the contents of that
264+
/// padding are not preserved. Concretely, given distinct `T` and `U` where
265+
/// `size_of::<T>() == size_of::<U>()`, the following code is not guaranteed to
266+
/// be sound:
267+
///
268+
/// ```rust,no_run
269+
/// # use core::mem::{MaybeUninit, transmute};
270+
/// # struct T; struct U;
271+
/// fn identity(t: T) -> T {
272+
/// unsafe {
273+
/// let u: MaybeUninit<U> = transmute(t);
274+
/// transmute(u)
275+
/// }
276+
/// }
277+
/// ```
278+
///
279+
/// If the representation of `t` contains initialized bytes at byte offsets
280+
/// where `U` contains padding bytes, these may not be preserved in
281+
/// `MaybeUninit<U>`. Transmuting `u` back to `T` (i.e., `transmute(u)` above)
282+
/// may thus be undefined behavior or yield a value different from `t` due to
283+
/// those bytes being lost. This is an active area of discussion, and this code
284+
/// may become sound in the future.
285+
///
286+
/// However, so long as no such byte offsets exist, then the preceding
287+
/// `identity` example *is* sound. In particular, since `[u8; N]` has no padding
288+
/// bytes, transmuting `t` to `MaybeUninit<[u8; size_of::<T>]>` and back will
289+
/// always produce the original value `t` again. This is true even if `t`
290+
/// contains [provenance]: the resulting value will have the same provenance as
291+
/// the original `t`.
292+
///
293+
/// Note a potential footgun: if `t` contains a reference, then there may be
294+
/// implicit reborrows of the reference any time it is copied, which may alter
295+
/// its provenance. In that case, the value returned by `identity` may not be
296+
/// exactly the same as its argument. However, even in this case, it remains
297+
/// true that `identity` behaves the same as a function that just returns `t`
298+
/// immediately (i.e., `fn identity<T>(t: T) -> T { t }`).
299+
///
300+
/// [provenance]: crate::ptr#provenance
301+
///
302+
/// [reference-byte]: ../../reference/memory-model.html#bytes
255303
#[stable(feature = "maybe_uninit", since = "1.36.0")]
256304
// Lang item so we can wrap other types in it. This is useful for coroutines.
257305
#[lang = "maybe_uninit"]

0 commit comments

Comments
 (0)