Module uninit::out_ref

source ·
Expand description

&out _ references in stable Rust!

An Out<'a, T> (&out T) is a write-only reference.

Its name is inspired by out parameters from various languages. It functions like &'a mut MaybeUninit<T>, except:

  • It can be safely constructed from a &mut T, since its API forbids writing MaybeUninit::uninit().
    • To avoid accidental memory leaks, T must be either Copy or wrapped in ManuallyDrop.
  • It supports Out<[T]>, whereas MaybeUninit<[T]> is currently invalid and must be written as [MaybeUninit<T>].

Much like &mut MaybeUninit, you can safely write to an Out and get a &mut T back - also without running any drop glue. Since it’s proven to be initialized, you can read and write through that reference without issue.

Interior Mutability

The whole design of Out references is to forbid any non-unsafe API that would allow writing MaybeUninit::uninit() garbage into the pointee. So, for instance, this crate does not offer any API like:

use ::core::{cell::Cell, mem::MaybeUninit};

// /!\ This is UNSOUND when combined with the `::uninit` crate!
fn swap_mb_uninit_and_cell<T> (
    p: &'_ MaybeUninit<Cell<T>>,
) -> &'_ Cell<MaybeUninit<T>>
{
    unsafe {
        // Safety: both `Cell` and `MaybeUninit` are `#[repr(transparent)]`
        ::core::mem::transmute(p)
    }
}

Indeed, if both such non-unsafe API and the uninit crate were present, then one could trigger UB with:

let mut x = [Cell::new(42)];
let at_mb_uninit_cell: &'_ MaybeUninit<Cell<u8>> =
    &x.as_out().as_ref_uninit()[0]
;
swap_mb_uninit_and_cell(at_mb_uninit_cell)
    .set(MaybeUninit::uninit()) // UB!
;

The author of the crate believes that such UB is the responsibility of the one who defined swap_mb_uninit_and_cell, and that in general that function is unsound: MaybeUninit-ness and interior mutability do not commute!

  • the Safety annotation in the given example only justifies that it is not breaking any layout-based validity invariants, but it is actually impossible to semantically prove that it is safe for these properties to commute.

If you are strongly convinced of the opposite, please file an issue (if there isn’t already one: since this question is not that clear the author is very likely to create an issue themself).

Modules

  • &out [_] slice iteration logic.

Structs

  • A write-only reference to a maybe-uninitialized T