What is the difference between Box, Rc, and Arc in Rust?
Understand the difference between Box, Rc and Arc in Rust: single ownership, single-thread sharing and thread-safe atomic sharing, with examples.
Expected Interview Answer
Box, Rc, and Arc are Rust smart pointers that allocate on the heap but differ in ownership: Box is a single-owner pointer, Rc gives multiple owners through reference counting on a single thread, and Arc is the thread-safe, atomically reference-counted version of Rc for sharing across threads.
Box<T> owns one value on the heap with zero runtime overhead and is used for recursive types, trait objects, or moving large data. Rc<T> keeps a non-atomic reference count so several owners can share read-only data within one thread, freeing the value when the last Rc drops. Arc<T> does the same but updates its count with atomic operations, making it safe to share across threads at a small performance cost. Rc and Arc give shared ownership, not shared mutability, so mutation needs a Cell, RefCell, or Mutex inside.
- Box enables heap allocation and recursive or trait-object types with no overhead
- Rc allows multiple owners of the same data on one thread
- Arc extends shared ownership safely across threads
- All three free memory automatically when the last owner drops
- Choosing the lightest one avoids paying for synchronization you do not need
AI Mentor Explanation
Box is the single match ball entrusted to one bowler at a time, one clear owner. Rc is a team strategy sheet photocopied for several players on the same side, all reading it, discarded once the last player hands it back. Arc is that sheet shared between two teams' dugouts with a locked counter, so updates to who holds it stay safe when both sides reach for it at once.
Step-by-Step Explanation
Step 1
Start with ownership needs
Decide whether the value has one owner or must be shared among several.
Step 2
One owner, heap allocation
Use Box<T> for recursive types, trait objects, or to move large data to the heap cheaply.
Step 3
Many owners, single thread
Use Rc<T> so several owners share the same data, with a non-atomic reference count.
Step 4
Many owners, across threads
Use Arc<T> when the shared data crosses thread boundaries; its count uses atomic operations.
Step 5
Add mutability if required
Wrap the inner value in RefCell (single-thread) or Mutex (multi-thread) since Rc and Arc are shared-immutable.
What Interviewer Expects
- Knows Box is single-owner with zero overhead
- Understands Rc uses non-atomic reference counting on one thread
- Knows Arc uses atomic counting for thread-safe sharing
- Explains that shared ownership is not shared mutability
- Can justify choosing Rc over Arc to avoid atomic overhead
Common Mistakes
- Using Arc everywhere and paying atomic cost when Rc suffices
- Trying to mutate through Rc or Arc without RefCell or Mutex
- Thinking Box allows multiple owners
- Creating reference cycles with Rc that leak memory (needs Weak)
- Believing Rc is thread-safe and sharing it across threads
Best Answer (HR Friendly)
“These are three ways Rust manages memory on the heap. Box is for data owned by exactly one part of the program, Rc lets several parts share the same data on a single thread, and Arc does the same but safely across multiple threads. You pick the simplest one that fits so you do not pay for features you do not need.”
Code Example
use std::rc::Rc;
use std::sync::Arc;
use std::thread;
// Box: single owner on the heap (recursive type)
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
// Rc: multiple owners, single thread
let shared = Rc::new(vec![1, 2, 3]);
let a = Rc::clone(&shared);
let b = Rc::clone(&shared);
println!("count = {}", Rc::strong_count(&shared)); // 3
println!("{:?} {:?}", a, b);
// Arc: multiple owners across threads
let data = Arc::new(vec![10, 20, 30]);
let mut handles = vec![];
for _ in 0..3 {
let d = Arc::clone(&data);
handles.push(thread::spawn(move || {
println!("sum = {}", d.iter().sum::<i32>());
}));
}
for h in handles { h.join().unwrap(); }
}Follow-up Questions
- Why can't you mutate the value behind an Rc directly?
- What is a reference cycle and how does Weak break it?
- How much overhead does Arc add compared with Rc?
- When would you use Rc<RefCell<T>> versus Arc<Mutex<T>>?
- Why is Box<dyn Trait> needed for trait objects?
MCQ Practice
1. Which smart pointer is safe to share across multiple threads?
Arc uses atomic reference counting, making it safe to clone and share ownership across threads; Rc is not thread-safe.
2. What is the main advantage of Rc over Arc on a single thread?
Rc uses non-atomic counting, so it is cheaper than Arc; on a single thread you do not need atomic synchronization.
3. How many owners does a Box<T> allow?
Box<T> is a single-ownership smart pointer; when it goes out of scope the heap value is freed.
Flash Cards
What is Box<T> for? — Single-owner heap allocation for recursive types, trait objects, or moving large data, with zero overhead.
How does Rc<T> track owners? — With a non-atomic reference count; the value drops when the last Rc goes out of scope. Single-thread only.
How does Arc<T> differ from Rc<T>? — Arc updates its reference count atomically, making shared ownership safe across threads at a small cost.
Do Rc/Arc allow mutation? — No, they give shared ownership, not mutability. Wrap the value in RefCell (Rc) or Mutex (Arc) to mutate.
Continue Learning
Related Interview Questions
What is the difference between fearless concurrency and data races in Rust?
hard
What is interior mutability in Rust and how do Cell and RefCell provide it?
hard
What are closures in Rust and how do Fn, FnMut, and FnOnce differ?
medium
What are the Send and Sync traits in Rust and what do they guarantee?
hard