@@ -255,51 +255,86 @@ use crate::{fmt, intrinsics, ptr, slice};
255255///
256256/// # Validity
257257///
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:
258+ /// `MaybeUninit<T>` has no validity requirements –- any sequence of [bytes] of
259+ /// the appropriate length, initialized or uninitialized, are a valid
260+ /// representation.
261+ ///
262+ /// Moving or copying a value of type `MaybeUninit<T>` (i.e., performing a
263+ /// "typed copy") will exactly preserve the contents, including the
264+ /// [provenance], of all non-padding bytes of type `T` in the value's
265+ /// representation.
266+ ///
267+ /// Therefore `MaybeUninit` can be used to perform a round trip of a value from
268+ /// type `T` to type `MaybeUninit<U>` then back to type `T`, while preserving
269+ /// the original value, if two conditions are met. One, type `U` must have the
270+ /// same size as type `T`. Two, for all byte offsets where type `U` has padding,
271+ /// the corresponding bytes in the representation of the value must be
272+ /// uninitialized.
273+ ///
274+ /// For example, due to the fact that the type `[u8; size_of::<T>]` has no
275+ /// padding, the following is sound for any type `T` and will return the
276+ /// original value:
267277///
268278/// ```rust,no_run
269279/// # use core::mem::{MaybeUninit, transmute};
270- /// # struct T; struct U;
280+ /// # struct T;
271281/// fn identity(t: T) -> T {
272282/// unsafe {
283+ /// let u: MaybeUninit<[u8; size_of::<T>()]> = transmute(t);
284+ /// transmute(u) // OK.
285+ /// }
286+ /// }
287+ /// ```
288+ ///
289+ /// Note: Copying a value that contains references may implicitly reborrow them
290+ /// causing the provenance of the returned value to differ from that of the
291+ /// original. This applies equally to the trivial identity function:
292+ ///
293+ /// ```rust,no_run
294+ /// fn trivial_identity<T>(t: T) -> T { t }
295+ /// ```
296+ ///
297+ /// Note: Moving or copying a value whose representation has initialized bytes
298+ /// at byte offsets where the type has padding may lose the value of those
299+ /// bytes, so while the original value will be preserved, the original
300+ /// *representation* of that value as bytes may not be. Again, this applies
301+ /// equally to `trivial_identity`.
302+ ///
303+ /// Note: Performing this round trip when type `U` has padding at byte offsets
304+ /// where the representation of the original value has initialized bytes may
305+ /// produce undefined behavior or a different value. For example, the following
306+ /// is unsound since `T` requires all bytes to be initialized:
307+ ///
308+ /// ```rust,no_run
309+ /// # use core::mem::{MaybeUninit, transmute};
310+ /// #[repr(C)] struct T([u8; 4]);
311+ /// #[repr(C)] struct U(u8, u16);
312+ /// fn unsound_identity(t: T) -> T {
313+ /// unsafe {
273314/// let u: MaybeUninit<U> = transmute(t);
274- /// transmute(u)
315+ /// transmute(u) // UB.
275316/// }
276317/// }
277318/// ```
278319///
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 }`).
320+ /// Conversely, the following is sound since `T` allows uninitialized bytes in
321+ /// the representation of a value, but the round trip may alter the value:
299322///
300- /// [provenance]: crate::ptr#provenance
323+ /// ```rust,no_run
324+ /// # use core::mem::{MaybeUninit, transmute};
325+ /// #[repr(C)] struct T(MaybeUninit<[u8; 4]>);
326+ /// #[repr(C)] struct U(u8, u16);
327+ /// fn non_identity(t: T) -> T {
328+ /// unsafe {
329+ /// // May lose an initialized byte.
330+ /// let u: MaybeUninit<U> = transmute(t);
331+ /// transmute(u)
332+ /// }
333+ /// }
334+ /// ```
301335///
302- /// [reference-byte]: ../../reference/memory-model.html#bytes
336+ /// [bytes]: ../../reference/memory-model.html#bytes
337+ /// [provenance]: crate::ptr#provenance
303338#[ stable( feature = "maybe_uninit" , since = "1.36.0" ) ]
304339// Lang item so we can wrap other types in it. This is useful for coroutines.
305340#[ lang = "maybe_uninit" ]
0 commit comments