Introduction
The Rc
(Reference Counted) class in Rust provides a way to enable multiple ownership of data by using reference counting. It is used for single-threaded scenarios where you want to share ownership of data and ensure the data is only deallocated when the last owner is dropped.
Using Rc
The Rc
class can be used to create a reference-counted smart pointer. Here is a basic example:
use std::rc::Rc;
fn main() {
let rc1 = Rc::new(5);
let rc2 = Rc::clone(&rc1);
println!("rc1: {}, rc2: {}", rc1, rc2);
}
// Output: rc1: 5, rc2: 5
Key Methods
Below are some of the key methods exposed by the Rc
class:
new
Creates a new Rc
instance.
use std::rc::Rc;
fn main() {
let rc = Rc::new(10);
println!("rc: {}", rc);
}
// Output: rc: 10
clone
Creates a new Rc
pointer to the same data, incrementing the reference count.
use std::rc::Rc;
fn main() {
let rc1 = Rc::new(15);
let rc2 = Rc::clone(&rc1);
println!("rc1: {}, rc2: {}", rc1, rc2);
}
// Output: rc1: 15, rc2: 15
strong_count
Returns the number of Rc
pointers to the same data.
use std::rc::Rc;
fn main() {
let rc1 = Rc::new(20);
let rc2 = Rc::clone(&rc1);
println!("Strong count: {}", Rc::strong_count(&rc1));
}
// Output: Strong count: 2
try_unwrap
Attempts to unwrap the value inside the Rc
. Succeeds only if there is exactly one strong reference.
use std::rc::Rc;
fn main() {
let rc = Rc::new(25);
match Rc::try_unwrap(rc) {
Ok(value) => println!("Unwrapped value: {}", value),
Err(_) => println!("Couldn't unwrap"),
}
}
// Output: Unwrapped value: 25
Example Usage
Example 1: Basic Usage
use std::rc::Rc;
fn main() {
let rc1 = Rc::new(30);
let rc2 = Rc::clone(&rc1);
println!("rc1: {}, rc2: {}", rc1, rc2);
}
// Output: rc1: 30, rc2: 30
Example 2: Checking Strong Count
use std::rc::Rc;
fn main() {
let rc1 = Rc::new(35);
let rc2 = Rc::clone(&rc1);
let rc3 = Rc::clone(&rc1);
println!("Strong count: {}", Rc::strong_count(&rc1));
}
// Output: Strong count: 3
Example 3: Conditional Unwrap
use std::rc::Rc;
fn main() {
let rc = Rc::new(40);
match Rc::try_unwrap(rc) {
Ok(value) => println!("Unwrapped value: {}", value),
Err(_) => println!("Couldn't unwrap"),
}
}
// Output: Unwrapped value: 40
Example 4: Using Rc with Structs
use std::rc::Rc;
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Rc::new(Point { x: 10, y: 20 });
let point_clone = Rc::clone(&point);
println!("Point: ({}, {})", point.x, point.y);
println!("Point clone: ({}, {})", point_clone.x, point_clone.y);
}
// Output:
// Point: (10, 20)
// Point clone: (10, 20)
Example 5: Sharing Ownership
use std::rc::Rc;
fn main() {
let rc = Rc::new(vec![1, 2, 3]);
let rc1 = Rc::clone(&rc);
let rc2 = Rc::clone(&rc);
println!("rc: {:?}", rc);
println!("rc1: {:?}", rc1);
println!("rc2: {:?}", rc2);
}
// Output:
// rc: [1, 2, 3]
// rc1: [1, 2, 3]
// rc2: [1, 2, 3]
Considerations
Rc
is not thread-safe. For thread-safe reference counting, useArc
instead.- Using
Rc
can help manage memory for shared data, but cycles in data structures can cause memory leaks sinceRc
does not support weak references.
See Also
- Arc - An atomic reference-counted smart pointer for shared ownership across threads.
- Box - A heap-allocated smart pointer.
- RefCell - A container for values that allows interior mutability with dynamic borrowing rules.
- Weak - A non-owning reference that is used in conjunction with
Rc
to prevent reference cycles.