Why this matters
Ownership in Rust normally means exactly one owner. Some data really is shared, though: a configuration used by many workers, a node in a graph with several incoming edges, a cached value referenced from several places. Borrowing does not fit when no single owner clearly outlives all the users. Rc<T> (reference counted) gives a value several owners: it stays alive as long as at least one of them does, and is freed automatically when the last one goes away.
Using Rc
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("shared text"));
println!("count after new: {}", Rc::strong_count(&a));
let b = Rc::clone(&a);
println!("count after clone: {}", Rc::strong_count(&a));
{
let c = Rc::clone(&a);
println!("inside block: {} ({} owners)", c, Rc::strong_count(&a));
}
println!("after block: {}", Rc::strong_count(&a));
println!("same allocation: {}", Rc::ptr_eq(&a, &b));
println!("length via deref: {}", b.len());
}
count after new: 1
count after clone: 2
inside block: shared text (3 owners)
after block: 2
same allocation: true
length via deref: 11
Rc::new(value)moves the value to the heap together with a reference count, starting at 1.Rc::clone(&a)makes a new owning pointer to the same allocation and increments the count. It does not copy the value. (a.clone()does the same thing;Rc::clone(&a)is the conventional spelling because it makes the cheapness visible.)- Dropping an
Rcdecrements the count. When it reaches 0, the value is dropped and the memory freed. Rc<T>implementsDeref, so you use the value through it directly.
In memory
a ----+
| heap allocation
b ----+---> +----------------------+
| | strong count: 3 |
c ----+ | weak count: 0 |
| value: "shared text" | (a String: ptr, len, cap -> its own buffer)
+----------------------+
The counts live next to the value in one allocation. Every clone and drop updates them, which costs a few instructions: cheap, but not free, unlike borrowing.
Rc gives shared, read-only access
Since several owners can reach the value at once, Rc<T> only gives out &T. Trying to mutate fails:
use std::rc::Rc;
fn main() {
let shared = Rc::new(vec![1, 2, 3]);
let other = Rc::clone(&shared);
shared.push(4);
println!("{:?}", other);
}
error[E0596]: cannot borrow data in an `Rc` as mutable
--> main.rs:6:5
|
6 | shared.push(4);
| ^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Vec<i32>>`
This is the aliasing rule from module 5 again: many owners means shared access, so no mutation. When shared data must change, combine Rc with RefCell (next lesson).
Sharing structure
A classic use: data structures where parts are shared. Two lists with a common tail:
use std::rc::Rc;
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let common = Rc::new(Cons(10, Rc::new(Cons(20, Rc::new(Nil)))));
let a = Cons(1, Rc::clone(&common));
let b = Cons(2, Rc::clone(&common));
println!("a = {:?}", a);
println!("b = {:?}", b);
println!("common has {} owners", Rc::strong_count(&common));
}
a = Cons(1, Cons(10, Cons(20, Nil)))
b = Cons(2, Cons(10, Cons(20, Nil)))
common has 3 owners
a: Cons(1, ---+
+--> Cons(10, --> Cons(20, --> Nil
b: Cons(2, ---+ (strong count 3: common, a, b)
With Box, the tail would have to be copied for the second list, since a Box has exactly one owner.
Rc is single-threaded
The count is updated with ordinary (non-atomic) instructions, which is fast but unsafe if two threads updated it at once. So Rc cannot be sent to another thread; the compiler enforces it, as you will see in module 14. The thread-safe version is Arc (atomic reference counting), with the same API.
When to use Rc
- Several parts of the program need to own the same data, and none of them clearly outlives the others.
- Graph-like structures where nodes have multiple parents.
- Immutable shared data (configuration, lookup tables) passed into many objects.
Prefer plain borrowing when one owner clearly outlives the users: &T is free and checked at compile time. Prefer Box when there is one owner. And beware of cycles: two Rcs pointing at each other keep each other alive forever (lesson 4).
Common mistakes
- Expecting
cloneto copy the data: it copies the pointer. If you want an independent copy, clone the inner value:(*a).clone(). - Trying to mutate through Rc: E0596. Use
Rc<RefCell<T>>, orRc::make_mutfor copy-on-write. - Using Rc across threads: E0277 "
Rc<...>cannot be sent between threads safely". UseArc. - Reaching for Rc to silence the borrow checker: often a clearer ownership structure (one owner, others borrow, or indexes into a Vec) is better.
Recap
Rc<T>gives a heap value multiple owners using a reference count;Rc::cloneincrements it, drop decrements it.- The value is freed when the count reaches zero.
Rcgives shared (&T) access only; mutation needsRefCellinside.Rc::strong_countandRc::ptr_eqinspect sharing.Rcis for single-threaded code;Arcis the thread-safe equivalent.
Going further: read the Rust Book chapter 15.4. Try Rc::try_unwrap(rc), which gives you the value back only if you hold the last reference.