Skip to content

Commit 54c57a2

Browse files
authored
Rollup merge of rust-lang#65948 - danielhenrymantilla:doc/maybe_uninit_ref_mut, r=RalfJung
Improve MaybeUninit::get_{ref,mut} documentation As mentioned in rust-lang#63568 (comment), `MaybeUninit`'s `get_{ref,mut}` documentation is lacking, so this PR attempts to fix that. That being said, and as @RalfJung mentions in that thread, > In particular, we should clarify that all the UB rules for these methods equally apply when calling the raw ptr methods and creating a reference manually. these other docs also need to be improved, which I can do in this PR ~~(hence the `[WIP]`)~~. Finally, since all these documentations are related to clearly establishing when dealing with uninitialized memory which patterns are known to be sound and which patterns are currently UB (that is, until, if ever, the rules around references to unintialized integers get relaxed, this documentation will treat them as UB, and advise against such patterns (_e.g._, it is not possible to use uninitialized buffers with the `Read` API)), I think that adding even more examples to the main documentation of `MaybeUninit` inherent definition wouldn't hurt either. ___ - [Rendered](http://dreamy-ritchie-99d637.netlify.com/core/mem/union.maybeuninit#method.get_ref)
2 parents d1fff4a + 67f2200 commit 54c57a2

File tree

1 file changed

+159
-8
lines changed

1 file changed

+159
-8
lines changed

src/libcore/mem/maybe_uninit.rs

+159-8
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)