Expand description
TheBox<T> type for heap allocation.
Box<T>, casually referred to as a ‘box’, provides the simplest form ofheap allocation in Rust. Boxes provide ownership for this allocation, anddrop their contents when they go out of scope. Boxes also ensure that theynever allocate more thanisize::MAX bytes.
§Examples
Move a value from the stack to the heap by creating aBox:
Move a value from aBox back to the stack bydereferencing:
Creating a recursive data structure:
#[derive(Debug)]enumList<T> { Cons(T, Box<List<T>>), Nil,}letlist: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));println!("{list:?}");This will printCons(1, Cons(2, Nil)).
Recursive structures must be boxed, because if the definition ofConslooked like this:
It wouldn’t work. This is because the size of aList depends on how manyelements are in the list, and so we don’t know how much memory to allocatefor aCons. By introducing aBox<T>, which has a defined size, we know howbigCons needs to be.
§Memory layout
For non-zero-sized values, aBox will use theGlobal allocator for its allocation. It isvalid to convert both ways between aBox and a raw pointer allocated with theGlobalallocator, given that theLayout used with the allocator is correct for the type and the rawpointer points to a valid value of the right type. More precisely, avalue: *mut T that hasbeen allocated with theGlobal allocator withLayout::for_value(&*value) may be convertedinto a box usingBox::<T>::from_raw(value). Conversely, the memory backing avalue: *mut Tobtained fromBox::<T>::into_raw may be deallocated using theGlobal allocator withLayout::for_value(&*value).
For zero-sized values, theBox pointer has to be non-null and sufficiently aligned. Therecommended way to build a Box to a ZST ifBox::new cannot be used is to useptr::NonNull::dangling.
On top of these basic layout requirements, aBox<T> must point to a valid value ofT.
So long asT: Sized, aBox<T> is guaranteed to be representedas a single pointer and is also ABI-compatible with C pointers(i.e. the C typeT*). This means that if you have extern “C”Rust functions that will be called from C, you can define thoseRust functions usingBox<T> types, and useT* as correspondingtype on the C side. As an example, consider this C header whichdeclares functions that create and destroy some kind ofFoovalue:
/* C header *//* Returns ownership to the caller */struct Foo* foo_new(void);/* Takes ownership from the caller; no-op when invoked with null */void foo_delete(struct Foo*);These two functions might be implemented in Rust as follows. Here, thestruct Foo* type from C is translated toBox<Foo>, which capturesthe ownership constraints. Note also that the nullable argument tofoo_delete is represented in Rust asOption<Box<Foo>>, sinceBox<Foo>cannot be null.
#[repr(C)]pub structFoo;#[unsafe(no_mangle)]pub extern"C"fnfoo_new() -> Box<Foo> { Box::new(Foo)}#[unsafe(no_mangle)]pub extern"C"fnfoo_delete(_:Option<Box<Foo>>) {}Even thoughBox<T> has the same representation and C ABI as a C pointer,this does not mean that you can convert an arbitraryT* into aBox<T>and expect things to work.Box<T> values will always be fully aligned,non-null pointers. Moreover, the destructor forBox<T> will attempt tofree the value with the global allocator. In general, the best practiceis to only useBox<T> for pointers that originated from the globalallocator.
Important. At least at present, you should avoid usingBox<T> types for functions that are defined in C but invokedfrom Rust. In those cases, you should directly mirror the C typesas closely as possible. Using types likeBox<T> where the Cdefinition is just usingT* can lead to undefined behavior, asdescribed inrust-lang/unsafe-code-guidelines#198.
§Considerations for unsafe code
Warning: This section is not normative and is subject to change, possiblybeing relaxed in the future! It is a simplified summary of the rulescurrently implemented in the compiler.
The aliasing rules forBox<T> are the same as for&mut T.Box<T>asserts uniqueness over its content. Using raw pointers derived from a boxafter that box has been mutated through, moved or borrowed as&mut Tis not allowed. For more guidance on working with box from unsafe code, seerust-lang/unsafe-code-guidelines#326.
§Editions
A special case exists for the implementation ofIntoIterator for arrays on the Rust 2021edition, as documentedhere. Unfortunately, it was later found that a similarworkaround should be added for boxed slices, and this was applied in the 2024 edition.
Specifically,IntoIterator is implemented forBox<[T]> on all editions, but specific callstointo_iter() for boxed slices will defer to the slice implementation on editions before2024:
// Rust 2015, 2018, and 2021:letboxed_slice: Box<[i32]> =vec![0;3].into_boxed_slice();// This creates a slice iterator, producing references to each value.foriteminboxed_slice.into_iter().enumerate() {let(i, x): (usize,&i32) = item;println!("boxed_slice[{i}] = {x}");}// The `boxed_slice_into_iter` lint suggests this change for future compatibility:foriteminboxed_slice.iter().enumerate() {let(i, x): (usize,&i32) = item;println!("boxed_slice[{i}] = {x}");}// You can explicitly iterate a boxed slice by value using `IntoIterator::into_iter`foriteminIntoIterator::into_iter(boxed_slice).enumerate() {let(i, x): (usize, i32) = item;println!("boxed_slice[{i}] = {x}");}Similar to the array implementation, this may be modified in the future to remove this override,and it’s best to avoid relying on this edition-dependent behavior if you wish to preservecompatibility with future versions of the compiler.