6.8 Smart Pointers and Heap Allocation

Rust includes various smart pointers that safely manage heap allocations. We will explore each in depth in later chapters. Below is a brief overview.

6.8.1 Box<T>: Simple Heap Allocation

Box<T> places data on the heap, storing only a pointer on the stack. When the Box<T> is dropped, the heap allocation is freed:

fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
} // `b` is dropped, and its heap data is freed

6.8.2 Recursive Types with Box<T>

Box<T> frequently appears in recursive data structures:

enum List {
    Cons(i32, Box<List>),
    Nil,
}

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

6.8.3 Rc<T>: Reference Counting for Single-Threaded Use

Rc<T> (reference count) allows multiple “owners” of the same data in single-threaded environments:

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("hello"));
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);
    println!("{}, {}, {}", a, b, c);
}

Rc::clone() does not create a deep copy; instead, it increments the reference count of the shared data. When the last Rc<T> is dropped, the data is freed.

6.8.4 Arc<T>: Atomic Reference Counting for Threads

Arc<T> is a thread-safe version of Rc<T> that uses atomic operations for the reference count:

use std::sync::Arc;
use std::thread;

fn main() {
    let a = Arc::new(String::from("hello"));
    let a1 = Arc::clone(&a);

    let handle = thread::spawn(move || {
        println!("{}", a1);
    });

    println!("{}", a);
    handle.join().unwrap();
}

6.8.5 RefCell<T> and Interior Mutability

RefCell<T> permits mutation through an immutable reference (interior mutability) with runtime borrow checks:

use std::cell::RefCell;

fn main() {
    let data = RefCell::new(5);

    {
        let mut v = data.borrow_mut();
        *v += 1;
    }

    println!("{}", data.borrow());
}

Combining Rc<T> and RefCell<T> allows multiple owners to mutate shared data in single-threaded code.