Question

Get an Owned Value from a Box in Rust

rustunsafe

Question

How can a function take ownership of a Box<T> and return the owned T inside it?

fn unbox<T>(value: Box<T>) -> T {
    // What should go here?
}

Box::into_raw appears related, and this code type-checks only inside an unsafe block:

fn unbox<T>(value: Box<T>) -> T {
    unsafe { *value.into_raw() }
}

Is that the correct implementation? Why does dereferencing the raw pointer require unsafe, and what does that mean for how Box<T> works?

Short Answer

You can move a value out of an owned Box<T> with *value:

fn unbox<T>(value: Box<T>) -> T {
    *value
}

This is safe Rust. Box::into_raw is for low-level ownership transfer and is not needed when you simply want the value inside the box.

Concept

A Box<T> is an owning smart pointer. It stores a T on the heap and ensures that the heap allocation is cleaned up when the Box is dropped.

When a function receives a Box<T> by value, it owns that box:

fn unbox<T>(value: Box<T>) -> T {
    *value
}

The expression *value dereferences the box and moves its contained T out. Because the box is owned, Rust knows this move is valid. It also knows that it must release the box's heap allocation afterward.

This differs from dereferencing a borrowed box:

fn read<T>(value: &Box<T>) -> &T {
    &**value
}

A borrowed Box<T> cannot generally give away its T, because the caller still owns the box.

Box::into_raw intentionally changes the situation. It consumes the Box<T> and returns a raw pointer, *mut T. At that point, Rust no longer automatically manages the allocation. The caller becomes responsible for using the pointer correctly and eventually reclaiming it, usually with Box::from_raw.

Mental Model

Think of Box<T> as a labelled, managed storage crate containing an item.

  • Owning a Box<T> means you hold the crate and are responsible for it.
  • Writing *value means: “Open the crate, take the item, and let Rust dispose of the now-empty crate correctly.”
  • Calling Box::into_raw() means: “Remove the management label and receive only the crate's storage address.”

After receiving a raw address, Rust cannot verify whether the address is valid, whether it has already been freed, or who is responsible for cleaning it up. That is why using raw pointers requires unsafe.

Take Quiz

Syntax and Examples

The normal, safe implementation is:

fn unbox<T>(value: Box<T>) -> T {
    *value
}

Example:

fn main() {
    let number = Box::new(42);
    let result = unbox(number);

    println!("{result}");
}

fn unbox<T>(value: Box<T>) -> T {
    *value
}

Output:

42

number cannot be used after the call because the Box<i32> was moved into unbox.

This also works for types that are not Copy:

fn main() {
    let message = Box::new(String::from("hello"));
    let message: String = *message;

    println!("{message}");
}

The String is moved out of the box; it is not cloned.

Step by Step Execution

Consider this code:

fn main() {
    let boxed = Box::new(String::from("Rust"));
    let text = unbox(boxed);
    println!("{text}");
}

fn unbox<T>(value: Box<T>) -> T {
    *value
}

Execution:

  1. String::from("Rust") creates a String value.
  2. Box::new(...) places that String in a heap allocation and returns Box<String>.
  3. Passing boxed to unbox moves ownership of the Box<String> into the parameter value.
  4. *value accesses the String stored by the box and moves it into the function's return value.
  5. Rust releases the box allocation. The returned String remains valid because ownership was moved to text.
  6. println! reads text.
  7. At the end of main, text is dropped, which cleans up the String's own contents.

The key point is that the box allocation and the value inside it have related but distinct cleanup responsibilities. Moving out T transfers ownership of T, while Rust still correctly frees the box allocation.

Real World Use Cases

Moving a value out of a box is useful when heap allocation was needed temporarily but the next stage can own the value directly.

  • Recursive data structures: A tree node may store children as Box<Node>. When consuming the tree, code can move a child node out of its box.
  • Trait objects: A function may receive Box<dyn Trait> and consume it to transfer ownership to another component.
  • Parser or compiler structures: Recursive expressions are often boxed. A transformation pass may consume a Box<Expr> and return an Expr.
  • Task or message processing: A queue may contain boxed jobs. A worker can take ownership of a job and execute it.
  • Reducing nesting: An API may return Box<ResultData> for a recursive or dynamically sized design, while later code needs the owned ResultData.
Take Quiz

Real Codebase Usage

In application code, developers usually write *boxed only when they truly need to consume the box and obtain its value.

A common pattern is destructuring a boxed enum in a match:

enum Expression {
    Number(i32),
    Negate(Box<Expression>),
}

fn evaluate(expression: Expression) -> i32 {
    match expression {
        Expression::Number(value) => value,
        Expression::Negate(inner) => -evaluate(*inner),
    }
}

inner is a Box<Expression>. *inner safely moves the contained Expression into evaluate.

Use Box::into_raw mainly at low-level boundaries, such as foreign-function interfaces (FFI), where another language temporarily receives an opaque pointer:

let boxed = Box::new(String::from("owned by Rust"));
let pointer: *mut String = Box::into_raw(boxed);

// Later, exactly once, when ownership returns to Rust:
unsafe {
    drop(Box::from_raw(pointer));
}

This pattern has a strict ownership contract: the raw pointer must be converted back exactly once, using the matching type and allocator assumptions.

Common Mistakes

Using into_raw just to extract the value

This compiles, but it is not the right implementation:

fn unbox<T>(value: Box<T>) -> T {
    unsafe { *value.into_raw() }
}

The raw pointer was created from a box, but no Box::from_raw is called to reclaim the box allocation. The T is moved out, while the allocation itself is leaked.

Use this instead:

fn unbox<T>(value: Box<T>) -> T {
    *value
}

Trying to move out through a borrow

This does not work for a non-Copy type:

fn broken(value: &Box<String>) -> String {
    **value
}

The function only borrowed the box, so it cannot move out the String. Accept Box<String> by value if the function should take ownership, or clone deliberately:

fn copy_text(value: &Box<String>) -> String {
    (**value).clone()
}

Assuming unsafe makes code automatically correct

An unsafe block tells the compiler that you, the programmer, are taking responsibility for the operation's safety requirements. It does not validate that those requirements are actually met.

Rebuilding a box more than once

This is undefined behavior:

let pointer = Box::into_raw(Box::new(10));

unsafe {
    let first = Box::from_raw(pointer);
    let second = Box::from_raw(pointer); // Invalid: same allocation owned twice
    drop(first);
    drop(second);
}

A pointer returned by Box::into_raw must be turned back into a Box at most once.

Comparisons

OperationOwnership resultSafe?Typical purpose
*boxed where boxed: Box<T>Moves out T; box allocation is cleaned upYesConsume a box and get its value
&*boxedBorrows TYesRead or pass the inner value without consuming the box
Box::into_raw(boxed)Returns *mut T; caller manages allocationYes to callFFI and low-level ownership transfer
*raw_pointerReads or moves through a raw pointerNo, requires unsafeLow-level code with manually proven pointer validity
Box::from_raw(pointer)Restores Box<T> ownershipNo, requires unsafeReclaim a pointer previously produced by into_raw

Box<T> dereferencing is safe because Rust tracks its ownership. Raw-pointer dereferencing is unsafe because raw pointers do not carry checked ownership or validity guarantees.

Take Quiz

Cheat Sheet

// Consume a Box<T> and return T.
fn unbox<T>(value: Box<T>) -> T {
    *value
}
  • Box<T> owns a heap-allocated T.
  • If you own the Box<T>, *boxed can move out the inner T.
  • If you only have &Box<T>, you can borrow with &**boxed, but cannot move out a non-Copy value.
  • Box::into_raw(boxed) consumes the box and yields *mut T.
  • Dereferencing *mut T requires unsafe because Rust cannot verify pointer validity.
  • A pointer from Box::into_raw should generally be recovered exactly once with Box::from_raw(pointer).
  • Do not use into_raw when *boxed is sufficient.

FAQ

Can I write *value without an unsafe block?

Yes. When value is an owned Box<T>, Rust safely moves out the contained T.

Why does *value.into_raw() require unsafe?

into_raw() returns a raw pointer. Rust cannot guarantee that arbitrary raw pointers are valid to dereference, so dereferencing one requires an explicit unsafe block.

Does *boxed clone the inner value?

No. It moves the inner value out of the box. No Clone implementation is required.

Does moving out of a box leak memory?

No. *boxed is handled safely by Rust: the inner value is moved, and the box allocation is released. Using into_raw without restoring ownership can leak the allocation.

Can I move a value out of &Box<T>?

Not in general. A shared borrow does not grant ownership. You can borrow the inner value, or clone it if cloning is appropriate.

When should I use Box::into_raw?

Use it when ownership must cross a low-level boundary, commonly an FFI boundary. You must clearly define who eventually calls Box::from_raw or otherwise correctly deallocates the allocation.

Is Box::into_raw itself unsafe?

No. Creating a raw pointer is safe. Operations that rely on the pointer being valid, such as dereferencing it or converting it back with Box::from_raw, are unsafe.

Take Quiz

Mini Project

Description

Build a small recursive expression evaluator. Recursive enum variants need indirection, so child expressions use Box<Expression>. Evaluating a negation consumes its boxed child with *inner.

Goal

Evaluate a recursive arithmetic expression while safely moving values out of Box<Expression>.

Requirements

Create an Expression enum with numeric, addition, and negation variants. Store recursive child expressions in Box<Expression>. Write an evaluate function that consumes an Expression and returns an i32. Use *inner to move a boxed child expression into a recursive call. Do not use unsafe or Box::into_raw.

Take Quiz

Keep learning

Related questions

View more