Skip to content

Commit 6067126

Browse files
Improved MaybeUninit::get_{ref,mut} documentation
1 parent dd2df8f commit 6067126

File tree

1 file changed

+151
-8
lines changed

1 file changed

+151
-8
lines changed

src/libcore/mem/maybe_uninit.rs

+151-8
Original file line numberDiff line numberDiff line change
@@ -509,26 +509,169 @@ impl<T> MaybeUninit<T> {
509509
self.as_ptr().read()
510510
}
511511

512-
/// Gets a reference to the contained value.
512+
/// Gets a shared reference to the contained value.
513+
///
514+
/// This can be useful when we want to access a `MaybeUninit` that has been
515+
/// initialized but don't have ownership of the `MaybeUninit` (preventing the use
516+
/// of `.assume_init()`).
513517
///
514518
/// # Safety
515519
///
516-
/// It is up to the caller to guarantee that the `MaybeUninit<T>` really is in an initialized
517-
/// state. Calling this when the content is not yet fully initialized causes undefined
518-
/// behavior.
520+
/// Calling this when the content is not yet fully initialized causes undefined
521+
/// behavior: it is up to the caller to guarantee that the `MaybeUninit<T>` really
522+
/// is in an initialized state.
523+
///
524+
/// # Examples
525+
///
526+
/// ### Correct usage of this method:
527+
///
528+
/// ```rust
529+
/// use ::std::mem::MaybeUninit;
530+
///
531+
/// let mut x = MaybeUninit::<Vec<u32>>::uninit();
532+
/// // Initialize `x`:
533+
/// unsafe { x.as_mut_ptr().write(vec![1, 2, 3]); }
534+
/// /* The above line can also be done without unsafe:
535+
/// x = MaybeUninit::new(vec![1, 2, 3]); // */
536+
/// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to
537+
/// // create a shared reference to it:
538+
/// let x: &Vec<u32> = unsafe {
539+
/// // # Safety
540+
/// //
541+
/// // - `x` has been initialized.
542+
/// x.get_ref()
543+
/// };
544+
/// assert_eq!(x, &vec![1, 2, 3]);
545+
/// ```
546+
///
547+
/// ### *Incorrect* usages of this method:
548+
///
549+
/// ```rust,no_run
550+
/// use std::mem::MaybeUninit;
551+
///
552+
/// let x = MaybeUninit::<Vec<u32>>::uninit();
553+
/// let x_vec: &Vec<u32> = unsafe { x.get_ref() };
554+
/// // We have created a reference to an uninitialized vector! This is undefined behavior.
555+
/// ```
556+
///
557+
/// ```rust,no_run
558+
/// use std::{cell::Cell, mem::MaybeUninit};
559+
///
560+
/// let b = MaybeUninit::<Cell<bool>>::uninit();
561+
/// // Initialize the `MaybeUninit` using `Cell::set`:
562+
/// unsafe {
563+
/// b.get_ref().set(true);
564+
/// // ^^^^^^^^^^^
565+
/// // Reference to an uninitialized `Cell<bool>`: UB!
566+
/// }
567+
/// ```
519568
#[unstable(feature = "maybe_uninit_ref", issue = "63568")]
520569
#[inline(always)]
521570
pub unsafe fn get_ref(&self) -> &T {
522571
&*self.value
523572
}
524573

525-
/// Gets a mutable reference to the contained value.
574+
/// Gets a mutable (unique) reference to the contained value.
575+
///
576+
/// This can be useful when we want to access a `MaybeUninit` that has been
577+
/// initialized but don't have ownership of the `MaybeUninit` (preventing the use
578+
/// of `.assume_init()`).
526579
///
527580
/// # Safety
528581
///
529-
/// It is up to the caller to guarantee that the `MaybeUninit<T>` really is in an initialized
530-
/// state. Calling this when the content is not yet fully initialized causes undefined
531-
/// behavior.
582+
/// Calling this when the content is not yet fully initialized causes undefined
583+
/// behavior: it is up to the caller to guarantee that the `MaybeUninit<T>` really
584+
/// is in an initialized state. For instance, `.get_mut()` cannot be used to
585+
/// initialize a `MaybeUninit`.
586+
///
587+
/// # Examples
588+
///
589+
/// ### Correct usage of this method:
590+
///
591+
/// ```rust
592+
/// use ::std::mem::MaybeUninit;
593+
///
594+
/// # unsafe extern "C" fn initialize_buffer (buf: *mut [u8; 2048]) { *buf = [0; 2048] }
595+
/// # #[cfg(FALSE)]
596+
/// extern "C" {
597+
/// /// Initializes *all* the bytes of the input buffer.
598+
/// fn initialize_buffer (buf: *mut [u8; 2048]);
599+
/// }
600+
///
601+
/// let mut buf = MaybeUninit::<[u8; 2048]>::uninit();
602+
/// // Initialize `buf`:
603+
/// unsafe { initialize_buffer(buf.as_mut_ptr()); }
604+
/// // Now we know that `buf` has been initialized; so we could `.assume_init()` it.
605+
/// // However, using `.assume_init()` may trigger a `memcpy` of the 2048 bytes.
606+
/// // To assert our buffer has been initialized without copying it, we upgrade
607+
/// // the `&mut MaybeUninit<[u8; 2048]>` to a `&mut [u8; 2048]`:
608+
/// let buf: &mut [u8; 2048] = unsafe {
609+
/// // # Safety
610+
/// //
611+
/// // - `buf` has been initialized.
612+
/// buf.get_mut()
613+
/// };
614+
/// // Now we can use `buf` as a normal slice:
615+
/// buf.sort_unstable();
616+
/// assert!(buf.is_sorted());
617+
/// ```
618+
///
619+
/// ### *Incorrect* usages of this method:
620+
///
621+
/// Do not use `.get_mut()` to initialize a value
622+
///
623+
/// ```rust,no_run
624+
/// use std::mem::MaybeUninit;
625+
///
626+
/// let mut b = MaybeUninit::<bool>::uninit();
627+
/// unsafe {
628+
/// *b.get_mut() = true;
629+
/// // We have created a (mutable) reference to an uninitialized `bool`!
630+
/// // This is undefined behavior.
631+
/// }
632+
/// ```
633+
///
634+
/// For instance, you cannot [`Read`] into an uninitialized buffer.
635+
///
636+
/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
637+
///
638+
/// ```rust,no_run
639+
/// use std::{io, mem::MaybeUninit};
640+
///
641+
/// fn read_chunk (reader: &'_ mut dyn io::Read) -> io::Result<[u8; 64]>
642+
/// {
643+
/// let mut buffer = MaybeUninit::<[u8; 64]>::uninit();
644+
/// reader.read_exact(unsafe { buffer.get_mut() })?;
645+
/// // ^^^^^^^^^^^^^^^^
646+
/// // (mutable) reference to uninitialized memory!
647+
/// // This is undefined behavior.
648+
/// Ok(buffer.assume_init())
649+
/// }
650+
/// ```
651+
///
652+
/// Nor can you use direct field access to do field-by-field gradual initialization.
653+
///
654+
/// ```rust,no_run
655+
/// use std::mem::MaybeUninit;
656+
///
657+
/// struct Foo {
658+
/// a: u32,
659+
/// b: u8,
660+
/// }
661+
///
662+
/// let foo: Foo = unsafe {
663+
/// let foo = MaybeUninit::<Foo>::uninit();
664+
/// ptr::write(&mut foo.get_mut().a as *mut u32, 1337);
665+
/// // ^^^^^^^^^^^^^
666+
/// // (mutable) reference to uninitialized memory!
667+
/// // This is undefined behavior.
668+
/// ptr::write(&mut foo.get_mut().b as *mut u8, 42);
669+
/// // ^^^^^^^^^^^^^
670+
/// // (mutable) reference to uninitialized memory!
671+
/// // This is undefined behavior.
672+
/// foo.assume_init()
673+
/// };
674+
/// ```
532675
// FIXME(#53491): We currently rely on the above being incorrect, i.e., we have references
533676
// to uninitialized data (e.g., in `libcore/fmt/float.rs`). We should make
534677
// a final decision about the rules before stabilization.

0 commit comments

Comments
 (0)