thecodex.expert · The Codex Family of Knowledge
Rust

Smart Pointers

When ownership does not fit the usual one-owner rule, Rust offers Box, Rc, and RefCell to relax it in specific, controlled ways.

Rust 1.96 Box, Rc, RefCell Last verified:
Canonical Definition

A smart pointer is a data structure that acts like a pointer but adds extra capabilities. Box<T> puts a value on the heap with ordinary single ownership, useful for recursive types or large data you don't want to copy. Rc<T> (reference counted) allows multiple owners of the same data by tracking a count of active references, freeing the data when the count hits zero — single-threaded only; the thread-safe equivalent is Arc<T>. RefCell<T> enforces the borrowing rules at runtime instead of compile time, allowing mutation through a reference that looks immutable, at the cost of a possible runtime panic instead of a compile error if the rules are violated.

Box<T>: heap allocation, single owner

Box is needed for recursive types, since Rust must know a type's size at compile time — a type that contains itself directly would be infinitely large, but a Box (a fixed-size pointer) breaks the cycle.

Rustbox_basics.rs
enum List {
    Cons(i32, Box<List>),   // Box breaks the infinite-size cycle
    Nil,
}

use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

Rc<T>: multiple owners via reference counting

Rc::clone doesn't deep-copy the data — it increments a reference count and returns a new pointer to the same heap allocation. The data is freed only when the count reaches zero.

Rustrc_basics.rs
use std::rc::Rc;

let a = Rc::new(String::from("shared data"));
println!("{}", Rc::strong_count(&a));   // 1

let b = Rc::clone(&a);   // increments the count, doesn't copy the string
println!("{}", Rc::strong_count(&a));   // 2

drop(b);
println!("{}", Rc::strong_count(&a));   // 1 again

RefCell<T>: interior mutability, checked at runtime

RefCell lets you mutate data even through what the type system sees as an immutable reference, by moving the borrow-checking rules to runtime — .borrow_mut() panics if another borrow is already active, exactly the rule the compiler would otherwise enforce statically.

Rustrefcell_basics.rs
use std::cell::RefCell;

let value = RefCell::new(5);

*value.borrow_mut() += 1;   // mutate through a "shared" RefCell
println!("{}", value.borrow());   // 6

// let a = value.borrow_mut();
// let b = value.borrow_mut();   // RUNTIME PANIC: already mutably borrowed

Sources

1
The Rust Project. The Rust Programming Language, "Smart Pointers", doc.rust-lang.org/book/ch15.