What does the box keyword do?
NOTE: This reply is a bit old. Since it talks about internals and unstable features, things have changed a little bit. The basic mechanism remains the same though, so the answer is still capable of explaining the undelying mechanisms of box
.
What does box x
usually uses to allocate and free memory?
The answer is the functions marked with lang items exchange_malloc
for allocation and exchange_free
for freeing. You can see the implementation of those in the default standard library at heap.rs#L112 and heap.rs#L125.
In the end the box x
syntax depends on the following lang items:
owned_box
on aBox
struct to encapsulate the allocated pointer. This struct does not need aDrop
implementation, it is implemented automatically by the compiler.exchange_malloc
to allocate the memory.exchange_free
to free the previously allocated memory.
This can be effectively seen in the lang items chapter of the unstable rust book using this no_std
example:
#![feature(lang_items, box_syntax, start, no_std, libc)]
#![no_std]
extern crate libc;
extern {
fn abort() -> !;
}
#[lang = "owned_box"]
pub struct Box<T>(*mut T);
#[lang = "exchange_malloc"]
unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
let p = libc::malloc(size as libc::size_t) as *mut u8;
// malloc failed
if p as usize == 0 {
abort();
}
p
}
#[lang = "exchange_free"]
unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) {
libc::free(ptr as *mut libc::c_void)
}
#[start]
fn main(argc: isize, argv: *const *const u8) -> isize {
let x = box 1;
0
}
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
Notice how Drop
was not implemented for the Box
struct? Well let's see the LLVM IR generated for main
:
define internal i64 @_ZN4main20hbd13b522fdb5b7d4ebaE(i64, i8**) unnamed_addr #1 {
entry-block:
%argc = alloca i64
%argv = alloca i8**
%x = alloca i32*
store i64 %0, i64* %argc, align 8
store i8** %1, i8*** %argv, align 8
%2 = call i8* @_ZN8allocate20hf9df30890c435d76naaE(i64 4, i64 4)
%3 = bitcast i8* %2 to i32*
store i32 1, i32* %3, align 4
store i32* %3, i32** %x, align 8
call void @"_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE"(i32** %x)
ret i64 0
}
The allocate
(_ZN8allocate20hf9df30890c435d76naaE
) was called as expected to build the Box
, meanwhile... Look! A Drop
method for the Box
(_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE
)! Let's see the IR for this method:
define internal void @"_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE"(i32**) unnamed_addr #0 {
entry-block:
%1 = load i32** %0
%2 = ptrtoint i32* %1 to i64
%3 = icmp ne i64 %2, 2097865012304223517
br i1 %3, label %cond, label %next
next: ; preds = %cond, %entry- block
ret void
cond: ; preds = %entry-block
%4 = bitcast i32* %1 to i8*
call void @_ZN10deallocate20he2bff5e01707ad50VaaE(i8* %4, i64 4, i64 4)
br label %next
}
There it is, deallocate
(ZN10deallocate20he2bff5e01707ad50VaaE
) being called on the compiler generated Drop!
Notice even on the standard library the Drop
trait is not implemented by user-code. Indeed Box
is a bit of a magical struct.
Before box
was marked as unstable, it was used as a shorthand for calling Box::new
. However, it's always been intended to be able to allocate arbitrary types, such as Rc
, or to use arbitrary allocators. Neither of these have been finalized, so it wasn't marked as stable for the 1.0 release. This is done to prevent supporting a bad decision for all of Rust 1.x.
For further reference, you can read the RFC that changed the "placement new" syntax and also feature gated it.
box
does exactly what Box::new()
does - it creates an owned box.
I believe that you can't find implementation of box
keyword because currently it is hardcoded to work with owned boxes, and Box
type is a lang item:
#[lang = "owned_box"]
#[stable(feature = "rust1", since = "1.0.0")]
#[fundamental]
pub struct Box<T>(Unique<T>);
Because it is a lang item, the compiler has special logic to handle its instantiation which it can link with box
keyword.
I believe that the compiler delegates box allocation to functions in alloc::heap
module.
As for what box
keyword does and supposed to do in general, Shepmaster's answer describes perfectly.