Rust Guide > Documentation > Collections > Rc

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, use Arc instead.
  • Using Rc can help manage memory for shared data, but cycles in data structures can cause memory leaks since Rc 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.