Skip to content
Box, Rc, Arc
step 1/5

Reading — step 1 of 5

Learn

~1 min readSmart Pointers and Interior Mutability

Smart pointers wrap a value with extra semantics (heap allocation, reference counting, etc.).

Box<T> — single owner, heap-allocated:

let b: Box<i32> = Box::new(5);
println!("{}", *b);          // 5 — auto-deref

// Useful for recursive types:
enum List {
    Cons(i32, Box<List>),
    Nil,
}

Without Box, the recursive enum would have infinite size — Box has known pointer size.

Rc<T> — single-threaded shared ownership (Reference Counted):

use std::rc::Rc;

let shared = Rc::new("hello");
let a = Rc::clone(&shared);    // increments refcount
let b = Rc::clone(&shared);

Rc::strong_count(&shared);     // 3

When the last Rc is dropped, the value is freed.

Arc<T> — atomically reference-counted, thread-safe:

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

let data = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];

for i in 0..3 {
    let data = Arc::clone(&data);
    handles.push(thread::spawn(move || {
        println!("thread {}: {:?}", i, data);
    }));
}

for h in handles { h.join().unwrap(); }

Comparison:

  • Box<T> — single owner, single thread, fast
  • Rc<T> — multiple owners, single thread, fast
  • Arc<T> — multiple owners, multi-thread, slightly slower (atomic ops)

Reference cycles with Rc/Arc cause memory leaks. Use Weak<T> for parent pointers in tree-shaped data:

use std::rc::{Rc, Weak};
use std::cell::RefCell;

struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,    // weak — doesn't keep parent alive
    children: RefCell<Vec<Rc<Node>>>,
}

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…