Overview
5 min read
In TypeScript and JavaScript every object is a garbage-collected, freely-shared, freely-mutable reference — the runtime makes every storage and lifetime decision for you. Rust hands those decisions back, and smart pointers are the types that encode them: Box<T> for heap allocation with a single owner, Rc<T>/Arc<T> for shared ownership, RefCell<T>/Mutex<T> and Cell<T> for interior mutability (mutating through a shared reference), Cow<'_, T> for clone-on-write, and Weak<T> for breaking reference cycles. The Deref trait is the quiet machinery that makes all of them feel like the value they wrap. This section maps each TypeScript habit — implicit boxing, aliased references, mutate-anywhere objects — onto the explicit Rust type that expresses the same intent at zero or near-zero runtime cost.
What You’ll Learn
Section titled “What You’ll Learn”- How to put a value on the heap with
Box<T>, why recursive types (cons lists, trees, ASTs) require it, and howBox<dyn Trait>owns a trait object so one container can hold many concrete types - How shared ownership works with
Rc<T>(single-threaded) andArc<T>(atomic, thread-safe), whycloneis a cheap reference-count bump rather than a deep copy, and how to read the live owner count withstrong_count - What interior mutability is, and how
RefCell<T>(single-thread, runtime borrow checks that panic on violation) andMutex<T>(thread-safe, blocking) let you mutate through a shared&reference - When to drop down to
Cell<T>forCopytypes —get/setof whole values with no references handed out and no runtime borrow tracking - How
Cow<'_, T>holds borrowed or owned data behind one type and allocates only at the moment you must mutate, eliminating needless copies on the hot path - How
Weak<T>breaks the reference cycles that would otherwise leakRc/Arcmemory, and howupgrade()safely turns a weak handle back into a strong one - How the
Deref/DerefMuttraits and deref coercion explain why&Stringworks where&stris expected and why you rarely write*on aBox - A repeatable decision procedure — single vs. shared owner, mutated through
&or not, one thread or many — that lands you on exactly the right smart pointer every time
Topics
Section titled “Topics”| Topic | Description |
|---|---|
Box<T>: Heap Allocation | Box<T> for heap allocation; recursive types (cons list, tree, AST); trait objects with Box<dyn Trait>. |
Shared Ownership with Rc/Arc | Rc<T> (single-thread) vs Arc<T> (atomic, thread-safe); shared ownership; strong_count; why cloning is a cheap reference bump. |
Interior Mutability: RefCell/Mutex | Interior mutability; RefCell<T> (single-thread, runtime borrow checks) vs Mutex<T> (thread-safe); borrow/borrow_mut panics. |
Cell<T> for Copy Types | Cell<T> for Copy types; get/set whole values without handing out references. |
Clone-on-Write with Cow | Cow<'_, T> clone-on-write; borrowed vs owned variants; avoiding needless allocation. |
Weak References with Weak<T> | Weak<T> to break reference cycles; upgrade(); a parent/child graph example. |
The Deref Trait | Deref/DerefMut; deref coercion; why &String works where &str is expected; Box deref. |
| Choosing a Smart Pointer | Decision guide: which smart pointer when — a table mapping each need to the type that satisfies it. |
Learning Objectives
Section titled “Learning Objectives”By the end of this section, you will be able to:
- Reach for
Box<T>to break the “infinite size” cycle of a recursive type, and useBox<dyn Trait>when different branches must return different concrete types - Choose between
Rc<T>andArc<T>based on whether the data crosses threads, and explain whyRc::clone/Arc::cloneare cheap count bumps that free deterministically when the last owner drops - Apply interior mutability deliberately:
Cell<T>forCopyvalues,RefCell<T>for single-threaded shared mutation,Mutex<T>/RwLock<T>across threads — and anticipate the runtime panic a doubledborrow_mut()produces - Combine a pointer with a cell idiomatically —
Rc<RefCell<T>>single-threaded,Arc<Mutex<T>>across threads — to model the “shared mutable object” that JavaScript gives you for free - Use
Cow<'_, str>to design APIs that return their input untouched on the common path and allocate only when they genuinely change it - Detect a reference cycle on sight and break it with
Weak<T>, recovering a live value throughupgrade() - Explain deref coercion well enough to predict when
&String,&Box<T>, or&Vec<T>will be accepted where a borrowed inner type is expected - Run the three-question decision procedure to pick the minimal correct smart pointer instead of defaulting to
Arc<Mutex<T>>
Prerequisites
Section titled “Prerequisites”- Section 05: Ownership — moves, borrows (
&/&mut), the one-writer-or-many-readers rule,Drop, and the reference-counting introduction; every smart pointer in this section is a deliberate departure from plain single ownership - Section 09: Generics & Traits — traits and especially trait objects, which
Box<dyn Trait>owns, plus the marker traitsSend/Syncthat decideRc-vs-ArcandRefCell-vs-Mutex
Estimated Time
Section titled “Estimated Time”- Reading: 4-5 hours
- Hands-on Practice: 4-5 hours
- Exercises: 2-3 hours
- Total: 10-12 hours
Tip: Read
00_box.mdfirst (it is the simplest pointer and introduces deref), then01_rc-arc.md→02_refcell-mutex.md→03_cell.mdto build up shared ownership and interior mutability, then04_cow.mdand05_weak.mdfor the two specialized cases. Finish with06_deref-trait.mdfor the trait that unifies them and07_comparison.mdfor the decision guide that ties everything together. The biggest mental shift for a TypeScript developer is that what JavaScript does invisibly — heap-allocate, share, and mutate every object — Rust makes you spell out, and each smart pointer is the explicit word for one of those previously-hidden choices.
Next: Section 11: Async → — Futures, async/await, and the runtimes that drive them, where Arc<Mutex<T>> from this section becomes the everyday shape of shared application state.