Skip to main content

Smart Pointers in Rust: Rc, Arc, RefCell, and Weak Explained

Created: April 24, 2026 CalmOps 3 min read

Why Smart Pointers Matter in Rust

Rust’s ownership model is strict by design: each value has one owner by default. But real systems often need shared ownership or mutation behind immutable APIs.

Smart pointers solve these advanced ownership needs while preserving Rust safety guarantees.

The Four Types You Should Know

  1. Box<T>: single owner, heap allocation.
  2. Rc<T>: shared ownership in single-threaded code.
  3. Arc<T>: shared ownership across threads.
  4. RefCell<T> / Mutex<T>: controlled interior mutability.

In practice, these are often combined.

Rc<T>: Shared Ownership (Single Thread)

Rc<T> (Reference Counted) tracks the number of strong references to heap data. When the strong count becomes zero, value is dropped.

use std::rc::Rc;

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

    let a = Rc::clone(&data);
    let b = Rc::clone(&data);
    println!("count={}", Rc::strong_count(&data)); // 3

    drop(a);
    println!("count={}", Rc::strong_count(&data)); // 2

    println!("{}", b);
}

Rc::clone is cheap: it increments count, not deep-copying payload.

Rc<T> + RefCell<T>: Shared Ownership with Mutation

Rc<T> alone gives immutable access. To mutate shared data in single-threaded code, use Rc<RefCell<T>>.

use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let counter = Rc::new(RefCell::new(0));

    let c1 = Rc::clone(&counter);
    let c2 = Rc::clone(&counter);

    *c1.borrow_mut() += 1;
    *c2.borrow_mut() += 2;

    println!("counter={}", counter.borrow()); // 3
}

RefCell enforces borrow rules at runtime. Violations panic.

Arc<T>: Shared Ownership Across Threads

Rc<T> is not Send/Sync. For multi-threaded sharing, use Arc<T> (Atomic Reference Counted).

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

fn main() {
    let msg = Arc::new(String::from("hello"));
    let mut handles = vec![];

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

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

Use Arc<Mutex<T>> when you need shared mutable state across threads.

Avoiding Reference Cycles with Weak<T>

Reference counting can leak memory through cycles:

  1. A points to B.
  2. B points to A.
  3. Strong counts never reach zero.

Break cycles using Weak<T> for back-references.

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

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}

Pattern:

  1. Parent owns children with Rc.
  2. Child references parent with Weak.

Common Combinations and When to Use Them

  1. Box<T>: recursive types, trait objects, large stack avoidance.
  2. Rc<T>: graph/tree sharing in single-threaded apps.
  3. Rc<RefCell<T>>: GUI state, interpreter environments.
  4. Arc<T>: immutable shared config across threads.
  5. Arc<Mutex<T>>: shared mutable service state.
  6. Arc<RwLock<T>>: many readers, few writers.

Performance Notes

  1. Rc is cheaper than Arc because no atomics.
  2. RefCell adds runtime borrow checks.
  3. Mutex introduces lock contention risk.
  4. Prefer ownership transfer/channels over shared mutable state when possible.

Typical Pitfalls

  1. Using Rc in async multithread executors where Arc is required.
  2. Overusing Arc<Mutex<T>> for everything.
  3. Forgetting Weak in graph-like structures.
  4. Calling unwrap() on poisoned mutex errors without handling strategy.

Practical Decision Guide

Ask in order:

  1. Do I need shared ownership?
  2. Do I need mutation?
  3. Is this multi-threaded?
  4. Could cycles happen?

Then choose pointer + mutability tool accordingly.

Conclusion

Rust smart pointers are not just syntax details. They are architecture tools for expressing ownership explicitly.

Mastering Rc, Arc, RefCell, and Weak gives you the ability to model complex data and concurrency patterns safely and efficiently.

Resources

Comments

Share this article

Scan to read on mobile