@@ -255,51 +255,80 @@ use crate::{fmt, intrinsics, ptr, slice};
255
255
///
256
256
/// # Validity
257
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>` .
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
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:
262
+ /// Using `MaybeUninit` to perform a round trip by transmuting a value of type
263
+ /// `T` first to type `MaybeUninit<U>` (where type `U` has the same size as `T`)
264
+ /// and then back to type `T` is guaranteed to be sound and to produce the
265
+ /// original value with its original [provenance] if and only if no bytes in the
266
+ /// representation of the value are initialized at byte offsets where type `U`
267
+ /// has padding.
268
+ ///
269
+ /// For example, due to the fact that the type `[u8; size_of::<T>]` has no
270
+ /// padding, the following is sound for any type `T`:
267
271
///
268
272
/// ```rust,no_run
269
273
/// # use core::mem::{MaybeUninit, transmute};
270
- /// # struct T; struct U;
274
+ /// # struct T;
271
275
/// fn identity(t: T) -> T {
272
276
/// unsafe {
277
+ /// let u: MaybeUninit<[u8; size_of::<T>()]> = transmute(t);
278
+ /// transmute(u) // OK.
279
+ /// }
280
+ /// }
281
+ /// ```
282
+ ///
283
+ /// Note: Copying a value that contains references may implicitly reborrow them,
284
+ /// and that may affect the value's provenance. In this respect, `identity`
285
+ /// behaves in exactly the same way as does the trivial identity function:
286
+ ///
287
+ /// ```rust,no_run
288
+ /// fn trivial_identity<T>(t: T) -> T { t }
289
+ /// ```
290
+ ///
291
+ /// Note: Moving or copying a value whose representation has initialized bytes
292
+ /// at byte offsets where the type has padding may lose the value of those
293
+ /// bytes, so while the original value will be preserved, the original
294
+ /// *representation* of that value as bytes may not be. Again, in this respect,
295
+ /// `identity` behaves in exactly the same way as does `trivial_identity`.
296
+ ///
297
+ /// Note: Performing this round trip when type `U` has padding at byte offsets
298
+ /// where the representation of the original value has initialized bytes may
299
+ /// produce undefined behavior or a different value. For example, the following
300
+ /// is unsound since `T` requires all bytes to be initialized:
301
+ ///
302
+ /// ```rust,no_run
303
+ /// # use core::mem::{MaybeUninit, transmute};
304
+ /// #[repr(C)] struct T([u8; 4]);
305
+ /// #[repr(C)] struct U(u8, u16);
306
+ /// fn unsound_identity(t: T) -> T {
307
+ /// unsafe {
273
308
/// let u: MaybeUninit<U> = transmute(t);
274
- /// transmute(u)
309
+ /// transmute(u) // UB.
275
310
/// }
276
311
/// }
277
312
/// ```
278
313
///
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 }`).
314
+ /// Conversely, the following is sound, since `T` allows uninitialized bytes in
315
+ /// the representation of a value, but the round trip may alter the value:
299
316
///
300
- /// [provenance]: crate::ptr#provenance
317
+ /// ```rust,no_run
318
+ /// # use core::mem::{MaybeUninit, transmute};
319
+ /// #[repr(C)] struct T(MaybeUninit<[u8; 4]>);
320
+ /// #[repr(C)] struct U(u8, u16);
321
+ /// fn non_identity(t: T) -> T {
322
+ /// unsafe {
323
+ /// // May lose an initialized byte.
324
+ /// let u: MaybeUninit<U> = transmute(t);
325
+ /// transmute(u)
326
+ /// }
327
+ /// }
328
+ /// ```
301
329
///
302
- /// [reference-byte]: ../../reference/memory-model.html#bytes
330
+ /// [bytes]: ../../reference/memory-model.html#bytes
331
+ /// [provenance]: crate::ptr#provenance
303
332
#[ stable( feature = "maybe_uninit" , since = "1.36.0" ) ]
304
333
// Lang item so we can wrap other types in it. This is useful for coroutines.
305
334
#[ lang = "maybe_uninit" ]
0 commit comments