---
title: How to Use Box<T> for Heap Allocation in Rust
summary: Use Box::new(value) to allocate data on the heap for recursive types or large data transfers.
lang: en
slug: how-to-use-boxt-for-heap-allocation-in-rust
published: 2026-04-17T14:12:14+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/how-to-use-boxt-for-heap-allocation-in-rust/
---

# How to Use Box<T> for Heap Allocation in Rust



## When the stack isn't enough

You are building a configuration parser. You read a 50MB JSON file and deserialize it into a `Config` struct. You pass that struct to a function. The compiler copies the entire 50MB onto the stack. Your program crashes with a stack overflow before it even starts.

Or you are defining a tree structure where a node contains other nodes. The compiler stops you immediately. It cannot figure out how big the type is because the type contains itself, which contains itself, forever.

Both problems point to the same solution. You need to move the data off the stack and onto the heap, and keep a small handle to it on the stack. That is what `Box<T>` does.

## The box is a handle to the heap

Rust puts most variables on the stack. The stack is fast and automatic, but it has strict limits. It is like a workbench: you can only have a few tools out at once, and they have to be small enough to fit on the surface. The heap is the warehouse. It is huge, but you have to manage access to it.

`Box<T>` is a pointer. It lives on the stack, but it points to data on the heap. Think of it like a key to a storage unit. The key is tiny and fits in your pocket. The storage unit can hold a sofa. When you pass the key to a friend, you are not moving the sofa. You are just giving them access to the unit.

`Box<T>` gives you heap allocation with the same ownership rules as stack variables. The compiler still tracks who owns the box, and it cleans up the heap memory when the box goes out of scope. You get the flexibility of the heap with the safety of the stack.

The box is the handle. The heap is the payload. Keep the handle small.

## Minimal example

Here is the simplest way to use a box. You wrap a value with `Box::new`, and the value moves to the heap.

```rust
fn main() {
    // Allocate an integer on the heap.
    // b is a pointer on the stack pointing to the heap data.
    let b = Box::new(42);

    // Access the value inside the box.
    // Rust automatically dereferences the box here.
    println!("Value inside the box: {}", b);
}
```

The variable `b` holds a pointer. The integer `42` lives on the heap. When you print `b`, Rust does not print the memory address. It prints the value inside. This happens because `Box` implements the `Deref` trait, which tells the compiler how to treat the box like the value it points to.

Convention: You rarely need to write `*b` explicitly. The compiler handles dereferencing for you. Writing `*b` is usually only needed when you want to move the value out of the box or when the compiler cannot infer the deref path. Trust the auto-deref.

## How the compiler handles the box

When you call `Box::new(value)`, the compiler generates code to request memory from the heap allocator. It copies the value into that memory and returns a pointer. The pointer is the `Box`.

Moving a `Box` is always cheap. If you have a `Box` containing a 1GB array, moving the `Box` only copies the pointer. The pointer is the size of a memory address, typically 8 bytes on modern systems. The 1GB of data stays exactly where it is on the heap.

This is a massive difference from stack values. If you move a large struct on the stack, the compiler copies every byte. If you box the struct, you only copy the pointer.

If you forget this and try to use the box after moving it, the compiler rejects you with `E0382` (use of moved value). The box follows the same move semantics as any other owned value. Once you move the box, the original variable is gone.

## Recursive types and infinite size

The most common reason to reach for `Box` is recursive types. The compiler needs to know the size of every type at compile time. This allows it to allocate stack space and calculate offsets. A type that contains itself would be infinitely large. The compiler cannot handle that.

```rust
#[derive(Debug)]
enum List {
    // A node holds a value and a pointer to the next node.
    // Without Box, the compiler cannot calculate the size.
    Cons(i32, Box<List>),
    Nil,
}

fn main() {
    // Build a small list: 1 -> 2 -> Nil.
    let list = List::Cons(
        1,
        Box::new(List::Cons(
            2,
            Box::new(List::Nil),
        )),
    );

    println!("{:?}", list);
}
```

If you remove the `Box` from the `Cons` variant, the compiler halts with `E0072` (recursive type has infinite size). The `Box` breaks the cycle. `Box<List>` has a known size: the size of a pointer. The compiler can calculate the size of `List` because it only needs to account for the pointer, not the entire nested structure.

The data structure still exists on the heap. Each node is allocated separately. The `Box` provides the link between nodes while keeping the type size finite.

Without the box, the type is infinite. With the box, it is just a pointer. Trust the pointer to break the cycle.

## Pitfalls and performance costs

`Box` is not free. Heap allocation takes time. It is slower than stack allocation because the allocator has to find free memory, track it, and handle fragmentation. Accessing data through a box also breaks cache locality. The CPU cache is optimized for data that sits close together on the stack. When you follow a pointer to the heap, the data might be far away, causing a cache miss.

Do not box small values just because you can. If you have a 16-byte struct, keeping it on the stack is faster. Box it only when you need to transfer ownership of large data, or when you need indirection for recursive types or trait objects.

Another pitfall is confusing `Box` with references. A reference (`&T`) borrows data. A `Box` owns data. If you only need to read or modify data temporarily, use a reference. References are zero-cost. They do not allocate memory, and they do not move data. Using a `Box` when a reference would do is a performance mistake.

Convention: When you see `Box<dyn Trait>`, read it as "I own a value that implements this trait, and I do not care about the concrete type." This is the standard way to achieve polymorphism in Rust. The community expects trait objects to be boxed because they need a fixed size for the vtable pointer. The box provides that fixed size while allowing the underlying type to vary.

Don't box everything. If a reference works, use it. Allocation is a cost you pay every time.

## Decision: Box vs references vs other pointers

Use `Box<T>` when you need heap allocation for a large value and want to transfer ownership. Use `Box<T>` for recursive types where the compiler needs a fixed-size pointer to break the cycle. Use `Box<dyn Trait>` when you need dynamic dispatch and want to own the trait object.

Reach for plain references (`&T` or `&mut T`) when you only need to read or modify data temporarily without taking ownership. References are zero-cost and the compiler handles the lifetime checks.

Pick `Rc<T>` when multiple parts of your program need to read the same data and you cannot determine a single owner at compile time. `Rc` adds reference counting overhead, so use it only when shared ownership is necessary.

Pick `Vec<T>` when you need a growable array of values. `Vec` manages a contiguous block of memory on the heap and handles resizing for you. Use `Box` for a single value, `Vec` for a collection.

Pick the tool that matches the ownership model. Ownership needs a box. Borrowing needs a reference. Sharing needs a count.

## Where to go next

- [What Is the Difference Between a Reference and a Smart Pointer in Rust?](/en/what-is-the-difference-between-a-reference-and-a-smart-pointer-in-rust/)
- [How Does Ownership Work with Function Arguments in Rust?](/en/how-does-ownership-work-with-function-arguments-in-rust/)
- [How to pass ownership through channels](/en/how-to-pass-ownership-through-channels/)