Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Box smart pointer is implemented?

Tags:

rust

The current implementation looks like this:

pub struct Box<
T: ?Sized,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global>(Unique<T>, A);

impl<T> Box<T> {


#[cfg(all(not(no_global_oom_handling)))]
#[inline(always)]
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
pub fn new(x: T) -> Self {
    #[rustc_box]
    Box::new(x)
}

Calling Box::new, does it not become recursive? Or is there any compiler magic going on?

like image 638
Vegeta Avatar asked Jun 24 '26 23:06

Vegeta


1 Answers

Box expression have special compiler support

Since Box expression have special compiler support, no explicit calls to exchange_malloc() and box_free() may show up in MIR, even if the compiler will generate them. We have to observe Rvalue::Box expressions and Box-typed drop-statements for that purpose.

The #[rustc_box] expression will be turned into a call to exchange_malloc by compiler at build/expr/as_rvalue.rs#L112:

...
ExprKind::Box { value } => {
    ...
    // malloc some memory of suitable size and align:
    let exchange_malloc = Operand::function_handle(
        tcx,
        tcx.require_lang_item(LangItem::ExchangeMalloc, Some(expr_span)),
        ty::List::empty(),
        expr_span,
    );
    ...
}

The definition of exchange_malloc will be linked by the language_item_table:

/// The allocator for unique pointers.
#[cfg(all(not(no_global_oom_handling), not(test)))]
#[lang = "exchange_malloc"]
#[inline]
unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
    let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
    match Global.allocate(layout) {
        Ok(ptr) => ptr.as_mut_ptr(),
        Err(_) => handle_alloc_error(layout),
    }
}

At the end of Box<T> lifetime, box_free_block will be called and finally lead to box_free:

#[cfg_attr(not(test), lang = "box_free")]
#[inline]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
// This signature has to be the same as `Box`, otherwise an ICE will happen.
// When an additional parameter to `Box` is added (like `A: Allocator`), this has to be added here as
// well.
// For example if `Box` is changed to  `struct Box<T: ?Sized, A: Allocator>(Unique<T>, A)`,
// this function has to be changed to `fn box_free<T: ?Sized, A: Allocator>(Unique<T>, A)` as well.
pub(crate) const unsafe fn box_free<T: ?Sized, A: ~const Allocator + ~const Destruct>(
    ptr: Unique<T>,
    alloc: A,
) {
    unsafe {
        let size = size_of_val(ptr.as_ref());
        let align = min_align_of_val(ptr.as_ref());
        let layout = Layout::from_size_align_unchecked(size, align);
        alloc.deallocate(From::from(ptr.cast()), layout)
    }
}
like image 53
Field Avatar answered Jun 27 '26 01:06

Field