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.In this page:
let a = Rc::new(value);
let b = Rc::clone(&a);
Rc::strong_count(&a);
let a = Arc::new(value); // thread-safe
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
// Bring `std::rc::Rc` into scope
use std::rc::Rc;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `shared`, set to `Rc::new(String::from("shared data"))`
let shared = Rc::new(String::from("shared data"));
// Declare an immutable variable `owner_two`, set to `Rc::clone(&shared)`
let owner_two = Rc::clone(&shared);
// Print "{} and {}" to the console, substituting in `shared, owner_two` (with a trailing newline)
println!("{} and {}", shared, owner_two);
// Print "Reference count: {}" to the console, substituting in `Rc::strong_count(&shared)` (with a trailing newline)
println!("Reference count: {}", Rc::strong_count(&shared));
}
Login to try C/C++/Java/PHP code in the editor
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
// Bring `std::rc::Rc` into scope
use std::rc::Rc;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `data`, set to `Rc::new(vec![1, 2, 3, 4, 5])`
let data = Rc::new(vec![1, 2, 3, 4, 5]);
// Declare an immutable variable `clone_a`, set to `Rc::clone(&data)`
let clone_a = Rc::clone(&data);
// Declare an immutable variable `clone_b`, set to `Rc::clone(&data)`
let clone_b = Rc::clone(&data);
// Print "Count: {}" to the console, substituting in `Rc::strong_count(&data)` (with a trailing newline)
println!("Count: {}", Rc::strong_count(&data));
// Print "{:?}" to the console, substituting in `clone_a` (with a trailing newline)
println!("{:?}", clone_a);
// Print "{:?}" to the console, substituting in `clone_b` (with a trailing newline)
println!("{:?}", clone_b);
}
Login to try C/C++/Java/PHP code in the editor
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
// Bring `std::sync::Arc` into scope
use std::sync::Arc;
// Bring `std::thread` into scope
use std::thread;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `shared`, set to `Arc::new(String::from("shared across threads"))`
let shared = Arc::new(String::from("shared across threads"));
// Declare an immutable variable `shared_clone`, set to `Arc::clone(&shared)`
let shared_clone = Arc::clone(&shared);
let handle = thread::spawn(move || {
// Print "From thread: {}" to the console, substituting in `shared_clone` (with a trailing newline)
println!("From thread: {}", shared_clone);
});
handle.join().unwrap();
// Print "From main: {}" to the console, substituting in `shared` (with a trailing newline)
println!("From main: {}", shared);
}
Login to try C/C++/Java/PHP code in the editor
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
// Bring `std::rc::Rc` into scope
use std::rc::Rc;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `a`, set to `Rc::new(10)`
let a = Rc::new(10);
// Print "Count after creating a: {}" to the console, substituting in `Rc::strong_count(&a)` (with a trailing newline)
println!("Count after creating a: {}", Rc::strong_count(&a));
{
// Declare an immutable variable `_b`, set to `Rc::clone(&a)`
let _b = Rc::clone(&a);
// Print "Count with b alive: {}" to the console, substituting in `Rc::strong_count(&a)` (with a trailing newline)
println!("Count with b alive: {}", Rc::strong_count(&a));
}
// Print "Count after b dropped: {}" to the console, substituting in `Rc::strong_count(&a)` (with a trailing newline)
println!("Count after b dropped: {}", Rc::strong_count(&a));
}
Login to try C/C++/Java/PHP code in the editor
- Trying to mutate data inside a plain
Rcdirectly --Rconly allows shared, immutable access on its own. - Using
Rcacross multiple threads, whenArc(atomic reference counting) is required for thread safety. - Creating a reference cycle with
Rc(two values owning each other), which leaks memory since the count never reaches zero.
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 ofRc, usable across multiple threads..clone()on anRc/Arcincreases 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: