← Back to Rust Course | Chapter 13: Smart Pointers & Unsafe | Lesson 2 of 7

Rc and Arc

Rc and Arc let more than one owner share the same value, counting how many are still using it before cleaning up.

Sharing Data with Rc

Rc::new(value) wraps a value for shared ownership; calling .clone() on it produces another owner pointing to the same data and increments an internal reference count.

Example: Sharing Data with Rc

markup
use std::rc::Rc;

fn main() {
    let shared = Rc::new(String::from("shared data"));
    let owner_two = Rc::clone(&shared);
    println!("{} and {}", shared, owner_two);
    println!("Reference count: {}", Rc::strong_count(&shared));
}

Rc Clone Is Cheap

Cloning an Rc does not deep-copy the underlying data -- it only increments the reference count, making it a very cheap operation regardless of how large the shared data is.

Example: Rc Clone Is Cheap

markup
use std::rc::Rc;

fn main() {
    let data = Rc::new(vec![1, 2, 3, 4, 5]);
    let clone_a = Rc::clone(&data);
    let clone_b = Rc::clone(&data);
    println!("Count: {}", Rc::strong_count(&data));
    println!("{:?}", clone_a);
    println!("{:?}", clone_b);
}

Using Arc for Thread Safety

Arc<T> behaves like Rc<T> but uses atomic operations for its reference count, making it safe to share across multiple threads, unlike plain Rc.

Example: Using Arc for Thread Safety

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

fn main() {
    let shared = Arc::new(String::from("shared across threads"));
    let shared_clone = Arc::clone(&shared);
    let handle = thread::spawn(move || {
        println!("From thread: {}", shared_clone);
    });
    handle.join().unwrap();
    println!("From main: {}", shared);
}

Value Dropped When Count Reaches Zero

The underlying data wrapped by Rc/Arc is only actually freed once every owner has been dropped and the reference count falls to zero, which happens automatically without manual bookkeeping.

Example: Value Dropped When Count Reaches Zero

markup
use std::rc::Rc;

fn main() {
    let a = Rc::new(10);
    println!("Count after creating a: {}", Rc::strong_count(&a));
    {
        let _b = Rc::clone(&a);
        println!("Count with b alive: {}", Rc::strong_count(&a));
    }
    println!("Count after b dropped: {}", Rc::strong_count(&a));
}
Common Mistakes
  1. Trying to mutate data inside a plain Rc directly -- Rc only allows shared, immutable access on its own.
  2. Using Rc across multiple threads, when Arc (atomic reference counting) is required for thread safety.
  3. Creating a reference cycle with Rc (two values owning each other), which leaks memory since the count never reaches zero.
Chapter Summary
  • Rc<T> (Reference Counted) enables multiple owners of the same heap-allocated value within a single thread.
  • Arc<T> (Atomically Reference Counted) is the thread-safe equivalent of Rc, usable across multiple threads.
  • .clone() on an Rc/Arc increases the reference count rather than deep-copying the underlying data.
  • The value is only actually dropped once its reference count reaches zero.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.