@@ -509,32 +509,183 @@ 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+ /// #![feature(maybe_uninit_ref)]
530+ /// use std::mem::MaybeUninit;
531+ ///
532+ /// let mut x = MaybeUninit::<Vec<u32>>::uninit();
533+ /// // Initialize `x`:
534+ /// unsafe { x.as_mut_ptr().write(vec![1, 2, 3]); }
535+ /// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to
536+ /// // create a shared reference to it:
537+ /// let x: &Vec<u32> = unsafe {
538+ /// // Safety: `x` has been initialized.
539+ /// x.get_ref()
540+ /// };
541+ /// assert_eq!(x, &vec![1, 2, 3]);
542+ /// ```
543+ ///
544+ /// ### *Incorrect* usages of this method:
545+ ///
546+ /// ```rust,no_run
547+ /// #![feature(maybe_uninit_ref)]
548+ /// use std::mem::MaybeUninit;
549+ ///
550+ /// let x = MaybeUninit::<Vec<u32>>::uninit();
551+ /// let x_vec: &Vec<u32> = unsafe { x.get_ref() };
552+ /// // We have created a reference to an uninitialized vector! This is undefined behavior.
553+ /// ```
554+ ///
555+ /// ```rust,no_run
556+ /// #![feature(maybe_uninit_ref)]
557+ /// use std::{cell::Cell, mem::MaybeUninit};
558+ ///
559+ /// let b = MaybeUninit::<Cell<bool>>::uninit();
560+ /// // Initialize the `MaybeUninit` using `Cell::set`:
561+ /// unsafe {
562+ /// b.get_ref().set(true);
563+ /// // ^^^^^^^^^^^
564+ /// // Reference to an uninitialized `Cell<bool>`: UB!
565+ /// }
566+ /// ```
519567 #[ unstable( feature = "maybe_uninit_ref" , issue = "63568" ) ]
520568 #[ inline( always) ]
521569 pub unsafe fn get_ref ( & self ) -> & T {
570+ intrinsics:: panic_if_uninhabited :: < 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+ /// #![feature(maybe_uninit_ref)]
593+ /// use std::mem::MaybeUninit;
594+ ///
595+ /// # unsafe extern "C" fn initialize_buffer(buf: *mut [u8; 2048]) { *buf = [0; 2048] }
596+ /// # #[cfg(FALSE)]
597+ /// extern "C" {
598+ /// /// Initializes *all* the bytes of the input buffer.
599+ /// fn initialize_buffer(buf: *mut [u8; 2048]);
600+ /// }
601+ ///
602+ /// let mut buf = MaybeUninit::<[u8; 2048]>::uninit();
603+ ///
604+ /// // Initialize `buf`:
605+ /// unsafe { initialize_buffer(buf.as_mut_ptr()); }
606+ /// // Now we know that `buf` has been initialized, so we could `.assume_init()` it.
607+ /// // However, using `.assume_init()` may trigger a `memcpy` of the 2048 bytes.
608+ /// // To assert our buffer has been initialized without copying it, we upgrade
609+ /// // the `&mut MaybeUninit<[u8; 2048]>` to a `&mut [u8; 2048]`:
610+ /// let buf: &mut [u8; 2048] = unsafe {
611+ /// // Safety: `buf` has been initialized.
612+ /// buf.get_mut()
613+ /// };
614+ ///
615+ /// // Now we can use `buf` as a normal slice:
616+ /// buf.sort_unstable();
617+ /// assert!(
618+ /// buf.chunks(2).all(|chunk| chunk[0] <= chunk[1]),
619+ /// "buffer is sorted",
620+ /// );
621+ /// ```
622+ ///
623+ /// ### *Incorrect* usages of this method:
624+ ///
625+ /// You cannot use `.get_mut()` to initialize a value:
626+ ///
627+ /// ```rust,no_run
628+ /// #![feature(maybe_uninit_ref)]
629+ /// use std::mem::MaybeUninit;
630+ ///
631+ /// let mut b = MaybeUninit::<bool>::uninit();
632+ /// unsafe {
633+ /// *b.get_mut() = true;
634+ /// // We have created a (mutable) reference to an uninitialized `bool`!
635+ /// // This is undefined behavior.
636+ /// }
637+ /// ```
638+ ///
639+ /// For instance, you cannot [`Read`] into an uninitialized buffer:
640+ ///
641+ /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
642+ ///
643+ /// ```rust,no_run
644+ /// #![feature(maybe_uninit_ref)]
645+ /// use std::{io, mem::MaybeUninit};
646+ ///
647+ /// fn read_chunk (reader: &'_ mut dyn io::Read) -> io::Result<[u8; 64]>
648+ /// {
649+ /// let mut buffer = MaybeUninit::<[u8; 64]>::uninit();
650+ /// reader.read_exact(unsafe { buffer.get_mut() })?;
651+ /// // ^^^^^^^^^^^^^^^^
652+ /// // (mutable) reference to uninitialized memory!
653+ /// // This is undefined behavior.
654+ /// Ok(unsafe { buffer.assume_init() })
655+ /// }
656+ /// ```
657+ ///
658+ /// Nor can you use direct field access to do field-by-field gradual initialization:
659+ ///
660+ /// ```rust,no_run
661+ /// #![feature(maybe_uninit_ref)]
662+ /// use std::{mem::MaybeUninit, ptr};
663+ ///
664+ /// struct Foo {
665+ /// a: u32,
666+ /// b: u8,
667+ /// }
668+ ///
669+ /// let foo: Foo = unsafe {
670+ /// let mut foo = MaybeUninit::<Foo>::uninit();
671+ /// ptr::write(&mut foo.get_mut().a as *mut u32, 1337);
672+ /// // ^^^^^^^^^^^^^
673+ /// // (mutable) reference to uninitialized memory!
674+ /// // This is undefined behavior.
675+ /// ptr::write(&mut foo.get_mut().b as *mut u8, 42);
676+ /// // ^^^^^^^^^^^^^
677+ /// // (mutable) reference to uninitialized memory!
678+ /// // This is undefined behavior.
679+ /// foo.assume_init()
680+ /// };
681+ /// ```
532682 // FIXME(#53491): We currently rely on the above being incorrect, i.e., we have references
533683 // to uninitialized data (e.g., in `libcore/fmt/float.rs`). We should make
534684 // a final decision about the rules before stabilization.
535685 #[ unstable( feature = "maybe_uninit_ref" , issue = "63568" ) ]
536686 #[ inline( always) ]
537687 pub unsafe fn get_mut ( & mut self ) -> & mut T {
688+ intrinsics:: panic_if_uninhabited :: < T > ( ) ;
538689 & mut * self . value
539690 }
540691
0 commit comments