21.1Introduction to smart pointers

Last updated June 13, 2026

You've been using one kind of pointer since chapter 9: the reference, &T and &mut T, which borrows a value without owning it. References are the right tool almost all the time, and the borrow checker keeps them safe. But references have a deliberate limitation: a reference never owns what it points to, it's always borrowing something that lives elsewhere, and it can never outlive that something. Most of the time that's exactly what you want. Occasionally it isn't, and that's what this chapter is about.

A smart pointer is a value that acts like a pointer, you reach through it to get at some data, but that also owns that data and adds behavior on top. The "smart" part is the extra behavior: a smart pointer might allocate on the heap, count how many owners share a value, free a resource when it's dropped, or enforce borrow rules at runtime instead of compile time. You've already used smart pointers without the label: String and Vec<T> are both smart pointers. Each owns a chunk of heap memory, hands you access to it, and frees it on drop. This chapter names the pattern and adds the three you'll reach for deliberately.

Why a plain reference isn't always enough

Three situations recur where &T can't do the job, and each gets a smart pointer in this chapter.

You need a value on the heap, by itself. A reference borrows something that already exists somewhere; it doesn't create heap storage. Sometimes you want to deliberately put a single value on the heap, because it's large, or because its type is recursive and the compiler can't size it on the stack. That's Box<T> (lesson 21.2).

You need more than one owner. Rust's rule since chapter 8 is that each value has exactly one owner. Usually that's a feature. But some data structures, a graph where several nodes point at a shared child, genuinely need a value owned jointly, with the value living until the last owner is gone. A reference can't express shared ownership; it owns nothing. That's Rc<T> (lesson 21.5).

You need to mutate through a shared reference. Chapter 9's iron law: you may have many &T or one &mut T, never both. Again, almost always right. But occasionally you need to mutate a value that's shared, and you can prove to yourself the access is safe even though the compiler can't see why at compile time. That's RefCell<T> and interior mutability (lesson 21.6), which moves the borrow check from compile time to run time. This finally explains the loose end from lesson 5.1, where "interior mutability" was named as a future topic.

What makes a pointer "smart"

Two traits give smart pointers their pointer-like feel, and both are this chapter's recurring characters.

Deref lets a smart pointer be used with the * dereference operator and, through deref coercion, lets &Box<T> work anywhere &T is wanted, just like &String works where &str is expected. That &String-to-&str magic from lesson 9.7, the one we named "deref coercion" and promised to explain, is Deref at work, and lesson 21.3 finally opens it up.

Drop lets a smart pointer run cleanup code when it goes out of scope, the mechanism behind the automatic freeing you've relied on since lesson 8.3. When a String frees its heap buffer or a File closes itself, that's Drop. Lesson 21.4 gives it the full treatment and names the broader pattern, RAII, that C++ refugees will recognize.

Key insight

A smart pointer is just a struct that owns some data and implements Deref (so you can reach through it like a pointer) and usually Drop (so it cleans up when it dies). There's nothing magic in Box, Rc, or RefCell that you couldn't, in principle, write yourself, and String and Vec already proved that by being smart pointers all along. This chapter isn't new language machinery so much as a few specific, carefully built structs that own data in ways a plain reference can't.

A map of the chapter

The three types divide cleanly by what they give you:

Box<T> gives you single ownership of a value on the heap. It's the simplest smart pointer, and the one you'll use most. Rc<T> gives you shared ownership through reference counting: many owners, freed when the last one drops. RefCell<T> gives you interior mutability: mutation through a shared reference, with the borrow rules checked at runtime. And they compose: Rc<RefCell<T>>, shared ownership and shared mutability, is a common combination for building flexible data structures, which we'll build toward.

The chapter ends where Rust's safety guarantees have their one honest gap: reference cycles (lesson 21.7). Two Rcs pointing at each other can leak memory, the one memory bug Rust's ownership system doesn't prevent, and Weak is the tool for breaking such cycles. Even here, "leak" is the worst case: a cycle wastes memory but never corrupts it. We'll close by being honest about exactly where the guarantees end.

Quiz time

Question #1

What distinguishes a smart pointer from a plain reference (&T)?

Show solution

A plain reference borrows a value it doesn't own and can't outlive it. A smart pointer owns the data it points to and adds behavior, allocating on the heap, counting owners, running cleanup on drop, or enforcing borrow rules at runtime. String and Vec<T> are smart pointers you've used all along: each owns heap data, lets you reach through to it, and frees it on drop.

Question #2

Name the three needs that the chapter's three smart pointers address.

Show solution

Box<T>: putting a single value on the heap (for large or recursively-sized types) with single ownership. Rc<T>: shared ownership, where a value needs more than one owner and lives until the last one drops. RefCell<T>: interior mutability, mutating a value through a shared reference, with the borrow rules enforced at runtime instead of compile time. Plain references can't express any of these.

Question #3

Which two traits make a type "smart," and what does each provide?

Show solution

Deref makes the type usable with * and enables deref coercion (so &Box<T> works where &T is expected, like &String working as &str). Drop lets the type run cleanup code when it goes out of scope (the automatic freeing behind String, Vec, and File). A smart pointer is essentially a struct owning data plus Deref and usually Drop.

The simplest smart pointer comes first. The next lesson (21.2) introduces Box<T>: heap allocation in one type, and the recursive data structures that can't be built without it.