Trait uninit::extension_traits::BoxUninit[][src]

pub trait BoxUninit: Sealed {
    type T;
    fn uninit() -> Self;
fn init(self, value: Self::T) -> Box<Self::T>;
fn try_alloc() -> Option<Self>; }
This is supported on crate features alloc or std only.
Expand description

Extension trait for uninitalized Box allocations and the optimized delayed-initialization pattern.

Associated Types

Required methods

Implementations on Foreign Types

Extension trait for uninitalized Box allocations and the optimized delayed-initialization pattern.

Optimized in-place heap initialization

The Box::uninit().init(...) delayed-initialization pattern is suprisingly effective in helping the optimizer inline the creation of the value directly into the heap.

  • In other words, this bundles ::copyless functionality.

  • For those wondering why Box::new(...) could not be made as efficient, the answer lies in temporaries: the ... temporary when calling Box::new() is created before attempting the allocation, and given that this allocation can fail / have side-effects, the optimizer is not allowed to reorder the creation of the temporary after the allocation, since it can change the semantics of the code for these corner (but not unreachable) cases. It is hence illegal for the optimizer to inline the creation of ... directly into the heap.

    Whereas Box::uninit().init(...) only creates the temporary after the allocation attempted in uninit() has succeeded, at which point it should be trivial for the optimizer to inline its creation directly into the heap.

  • Note, however, that this property cannot be guaranteed from a library perspective; for instance, the heap-inlined initialization does not seem to happen when the optimization level (opt-level) is less than 2. Inversely, the author has observed that the heap-inlined initialization does seem to kick in when compiling with -C opt-level=2 (or 3), e.g., when running on --release.

Example
use ::uninit::prelude::*;

let ft: Box<u8> = Box::uninit().init(42);
assert_eq!(*ft, 42);

This optimization can even allow creating arrays too big to fit in the stack.

  • For instance, the following implementation panics:

    fn alloc_big_boxed_array () -> Box<[u64; 10_000_000]>
    {
        // This can panic because of the `[0; 10_000_000]` stack
        // temporary overflowing the stack.
        Box::new([0; 10_000_000])
    }
  • Whereas the following one does not (doc-tested with RUSTDOCFLAGS=-Copt-level=2):

    fn alloc_big_boxed_array () -> Box<[u64; 10_000_000]>
    {
        // But this works fine, since there is no stack temporary!
        Box::uninit().init([0; 10_000_000])
    }

Handling allocation failure

A neat side-effect of this implementation is to expose the intermediate state of Box::try_alloc(), which yields an Option<Box<MaybeUninit<T>>> depending on whether the attempted allocation succeeded or not.

Example
use ::uninit::prelude::*;

let buf: Box<[u8; ::core::i32::MAX as _]> = match Box::try_alloc() {
    | Some(uninit) => uninit.init([0; ::core::i32::MAX as _]),
    | None => {
        panic!("Failed to allocate 2GB of memory");
    }
};

Idiomatic allocation-failure unwrapping of BoxUninit::try_alloc().

Attempts to Box-allocate memory for T, without initializing it.

Returns None when the allocation fails.

Safely initialize a Box::MaybeUninit<T> by providing a value: T (that can be inlined into the Box), and safely return the ergonomic Box<T> witness of that initialization.

Implementors