Skip to main content

Module boxed

Module boxed 

1.0.0 · Source
Expand description

The Box<T> type for heap allocation.

Box<T>, casually referred to as a ‘box’, provides the simplest form of heap allocation in Rust. Boxes provide ownership for this allocation, and drop their contents when they go out of scope. Boxes also ensure that they never allocate more than isize::MAX bytes.

§Examples

Move a value from the stack to the heap by creating a Box:

let val: u8 = 5;
let boxed: Box<u8> = Box::new(val);

Move a value from a Box back to the stack by dereferencing:

let boxed: Box<u8> = Box::new(5);
let val: u8 = *boxed;

Creating a recursive data structure:

#[derive(Debug)]
enum List<T> {
    Cons(T, Box<List<T>>),
    Nil,
}

let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{list:?}");

This will print Cons(1, Cons(2, Nil)).

Recursive structures must be boxed, because if the definition of Cons looked like this:

ⓘ
Cons(T, List<T>),

It wouldn’t work. This is because the size of a List depends on how many elements are in the list, and so we don’t know how much memory to allocate for a Cons. By introducing a Box<T>, which has a defined size, we know how big Cons needs to be.

§Memory layout

For non-zero-sized values, a Box<T, A> will use its allocator of type A for its allocation. It is valid to convert both ways between a Box and a raw pointer allocated with A, so long as:

  • the size of the pointee, as reported by size_of_val, is non-zero,
  • the Layout::for_value of the pointee fits the memory block, and
  • the pointee is a valid instance of T (for the given metadata of the pointer).

For zero-sized values, a Box<T, A> may use any sufficiently aligned non-null pointer. While it is sound to convert a zero-sized allocation from A into a Box, as long as it is backed by a valid value of T, doing so may lead to memory leaks, as the destructor of Box will not call deallocate on zero-sized values. It is recommended to use ptr::dangling instead.

Likewise, manually deallocating the raw pointer acquired from a Box is only sound if the size of the pointee is non-zero, as zero-sized values may be represented by a pointer that is not currently allocated by that allocator. It is recommended to pair Box::into_raw_with_allocator with Box::from_raw_in, rather than doing this.

Note that even the Global allocator might not use dangling pointers for zero-sized allocations, even though most implementations do this.

So long as T: Sized, a Box<T> is guaranteed to be represented as a single pointer and is also ABI-compatible with C pointers (i.e. the C type T*). This means that if you have extern “C” Rust functions that will be called from C, you can define those Rust functions using Box<T> types, and use T* as corresponding type on the C side. As an example, consider this C header which declares functions that create and destroy some kind of Foo value:

/* 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, the struct Foo* type from C is translated to Box<Foo>, which captures the ownership constraints. Note also that the nullable argument to foo_delete is represented in Rust as Option<Box<Foo>>, since Box<Foo> cannot be null.

#[repr(C)]
pub struct Foo;

#[unsafe(no_mangle)]
pub extern "C" fn foo_new() -> Box<Foo> {
    Box::new(Foo)
}

#[unsafe(no_mangle)]
pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}

Even though Box<T> has the same representation and C ABI as a C pointer, this does not mean that you can convert an arbitrary T* into a Box<T> and expect things to work. Box<T> values will always be fully aligned, non-null pointers. Moreover, the destructor for Box<T> will attempt to free the value with the global allocator. In general, the best practice is to only use Box<T> for pointers that originated from the global allocator.

Important. At least at present, you should avoid using Box<T> types for functions that are defined in C but invoked from Rust. In those cases, you should directly mirror the C types as closely as possible. Using types like Box<T> where the C definition is just using T* can lead to undefined behavior, as described in rust-lang/unsafe-code-guidelines#198.

§Considerations for unsafe code

Warning: This section is not normative and is subject to change, possibly being relaxed in the future! It is a simplified summary of the rules currently implemented in the compiler.

The aliasing rules for Box<T> are the same as for &mut T. Box<T> asserts uniqueness over its content. Using raw pointers derived from a box after that box has been mutated through, moved or borrowed as &mut T is not allowed. For more guidance on working with box from unsafe code, see rust-lang/unsafe-code-guidelines#326.

§Editions

A special case exists for the implementation of IntoIterator for arrays on the Rust 2021 edition, as documented here. Unfortunately, it was later found that a similar workaround should be added for boxed slices, and this was applied in the 2024 edition.

Specifically, IntoIterator is implemented for Box<[T]> on all editions, but specific calls to into_iter() for boxed slices will defer to the slice implementation on editions before 2024:

ⓘ
// Rust 2015, 2018, and 2021:

let boxed_slice: Box<[i32]> = vec![0; 3].into_boxed_slice();

// This creates a slice iterator, producing references to each value.
for item in boxed_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:
for item in boxed_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`
for item in IntoIterator::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 preserve compatibility with future versions of the compiler.

Structs§

Box
A pointer type that uniquely owns a heap allocation of type T.
BoxedArrayIntoIter
A by-value Box<[T; N]> iterator.
ThinBoxExperimental
ThinBox.

Functions§

box_assume_init_into_vec_unsafe 👻 Experimental
Helper for vec!.