Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

Rust Dictionary

  1. Home
  2. Rust Dictionary
  3. Rc<T> / Arc<T>

Rc<T> / Arc<T>

Since: Rust 1.0(2015)

Rc<T> and Arc<T> are reference-counted smart pointers that allow a single value to have multiple owners. Rc is for single-threaded use, while Arc is the thread-safe version.

Syntax

use std::rc::Rc;
use std::sync::Arc;

// Rc: creates multiple owners in a single-threaded context.
let a = Rc::new(value);
let b = Rc::clone(&a); // Increments the reference count.

// Arc: creates multiple owners across threads.
let a = Arc::new(value);
let b = Arc::clone(&a); // Can be shared across threads.

// Check the reference count
Rc::strong_count(&a)
Arc::strong_count(&a)

Method List

MethodDescription
Rc::new(v)Wraps the value v in an Rc. The reference count starts at 1.
Rc::clone(&a)Creates a new Rc pointer, incrementing the reference count.
Rc::strong_count(&a)Returns the current strong reference count.
Rc::weak_count(&a)Returns the current weak reference count (for Weak references).
Arc::new(v)Wraps the value v in an Arc, allowing safe sharing across threads.
Arc::clone(&a)Creates a new Arc pointer, atomically incrementing the reference count.

Sample Code

sample_rc_arc.rs
use std::rc::Rc;
use std::sync::Arc;
use std::thread;

fn main() {
    // Rc: multiple owners in a single-threaded context.
    let rc_val = Rc::new(String::from("shared"));
    let rc_a = Rc::clone(&rc_val);
    let rc_b = Rc::clone(&rc_val);

    println!("{}", rc_val); // shared
    println!("{}", rc_a); // shared
    println!("count: {}", Rc::strong_count(&rc_val)); // count: 3

    drop(rc_a);
    println!("after drop: {}", Rc::strong_count(&rc_val)); // after drop: 2

    // Arc: shared across threads.
    let arc_val = Arc::new(42);
    let handles: Vec<_> = (0..3).map(|i| {
        let val = Arc::clone(&arc_val);
        thread::spawn(move || {
            println!("thread {}: {}", i, val);
        })
    }).collect();

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

Compile with the following command:

rustc rc_arc.rs
./rc_arc
shared
shared
count: 3
after drop: 2
thread 0: 42
thread 1: 42
thread 2: 42
rc_arc2.rs
use std::rc::{Rc, Weak};
use std::cell::RefCell;

struct Node {
    name: String,
    child: Option<Rc<RefCell<Node>>>,
    parent: Option<Weak<RefCell<Node>>>, // Use Weak to prevent circular references.
}

impl Node {
    fn new(name: &str) -> Rc<RefCell<Node>> {
        Rc::new(RefCell::new(Node {
            name: name.to_string(),
            child: None,
            parent: None,
        }))
    }
}

fn main() {
    let parent = Node::new("user_a");
    let child = Node::new("user_b");

    // Set a weak reference to parent in the child node.
    child.borrow_mut().parent = Some(Rc::downgrade(&parent));
    // Set a strong reference to child in the parent node.
    parent.borrow_mut().child = Some(Rc::clone(&child));

    println!("parent count: {}", Rc::strong_count(&parent)); // 1
    println!("child count:  {}", Rc::strong_count(&child)); // 2 (owned by parent)

    // Temporarily upgrade Weak to a strong reference.
    let parent_ref = child.borrow().parent.as_ref().and_then(|w| w.upgrade());
    if let Some(p) = parent_ref {
        println!("child's parent: {}", p.borrow().name);
    }
} // parent is freed → child's strong_count also reaches 0 (no memory leak)

Compile with the following command:

rustc rc_arc2.rs
./rc_arc2
parent count: 1
child count:  2
child's parent: user_a
rc_arc3.rs
use std::sync::Arc;
use std::thread;

fn main() {
    let scores = Arc::new(vec![85, 92, 78, 96, 88]);

    let handles: Vec<_> = (0..scores.len()).map(|i| {
        let data = Arc::clone(&scores);
        thread::spawn(move || {
            println!("thread {}: score = {}", i, data[i]);
            data[i]
        })
    }).collect();

    let total: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
    println!("total: {}", total); // total: 439
    println!("avg: {:.1}", total as f64 / scores.len() as f64); // avg: 87.8
}

Compile with the following command:

rustc rc_arc3.rs
./rc_arc3
thread 0: score = 85
thread 1: score = 92
thread 2: score = 78
thread 3: score = 96
thread 4: score = 88
total: 439
avg: 87.8

Common Mistakes

Common Mistake 1: Rc cannot be sent between threads (Send error)

Rc does not implement the Send trait, so passing it to a thread causes a compile error.

rc_arc_mistake1.rs
use std::rc::Rc;
use std::thread;

fn main() {
    let val = Rc::new(42);
    let v = Rc::clone(&val);
    // Rc cannot be sent between threads.
    thread::spawn(move || {
        println!("{}", v);
    });
}

Compile with the following command:

rustc rc_arc_mistake1.rs
error[E0277]: `Rc<i32>` cannot be sent between threads safely

Use Arc when sharing data across threads.

rc_arc_fix1.rs
use std::sync::Arc;
use std::thread;

fn main() {
    let val = Arc::new(42);
    let v = Arc::clone(&val);
    let handle = thread::spawn(move || {
        println!("{}", v);
    });
    handle.join().unwrap();
}

Compile with the following command:

rustc rc_arc_fix1.rs
./rc_arc_fix1
42

Common Mistake 2: Circular references with Rc cause memory leaks

When two Rc values reference each other, the reference count never reaches zero and memory is never freed.

rc_arc_mistake2.rs
use std::rc::Rc;
use std::cell::RefCell;

struct Node {
    next: Option>,
}

fn main() {
    let a = Rc::new(RefCell::new(Node { next: None }));
    let b = Rc::new(RefCell::new(Node { next: None }));

    // Set a reference from a to b.
    a.borrow_mut().next = Some(Rc::clone(&b));
    b.borrow_mut().next = Some(Rc::clone(&a));

    // Even after leaving the scope, the count never reaches 0 — memory is not freed.
    println!("a count: {}", Rc::strong_count(&a)); // 2
    println!("b count: {}", Rc::strong_count(&b)); // 2
}

Compile with the following command:

rustc rc_arc_mistake2.rs
./rc_arc_mistake2
a count: 2
b count: 2

Making one side a Weak reference prevents the cycle (see rc_arc2.rs in the sample code above).

Common Mistake 3: .clone() vs Rc::clone(&a)

Both .clone() and Rc::clone(&a) increment the reference count and return a new Rc pointer — they behave identically. However, Rc::clone(&a) makes it explicit that this is an Rc clone, which communicates intent more clearly to readers of the code.

rc_arc_clone.rs
use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("hello"));

    // Both do the same thing — they increment the reference count.
    let b = Rc::clone(&a); // This form is more commonly used.
    let c = a.clone(); // This also works.

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

Compile with the following command:

rustc rc_arc_clone.rs
./rc_arc_clone
count: 3
a=hello, b=hello, c=hello

Notes

In Rust's ownership system, a value normally has exactly one owner. Using Rc<T> or Arc<T> allows multiple owners. When all references are dropped (the count reaches zero), the memory is automatically freed.

Rc cannot be shared across threads because it does not implement the Send trait. The Send trait is a marker that indicates a value can be safely sent to another thread. Arc is used when sharing data between threads.

Circular references with Rc will cause memory leaks. If there is a risk of a cycle, make one side a Weak<T> (weak reference) to prevent the reference count from cycling.

If you find any errors or copyright issues, please .