SoFunction
Updated on 2025-04-09

Interpretation of Rust's Rc: Intelligent pointer method to implement multiple ownership

Why do you need more ownership?

Usually, we are used to having only one owner per value so that the compiler can automatically free resources when the value leaves scope. However, in some data structures, a node may be referenced simultaneously by multiple other structures—such as a node in a graph structure or part of a shared linked list.

For this scenario, if you only use a single ownership, the compiler will refuse to compile because of ownership transfer, or you have to introduce complex lifecycle annotations to ensure that all references are legal.

Consider a simple example:

  • You have a link lista, which contains the numbers 5 and 10; then you want to create two other linked listsbandc, they all shareaThis sub-link list.
  • IfBox<T>to implement linked lists, since ownership will be transferred when moved,aCannot be simultaneouslybandcowned, resulting in a compile error.

The core idea of ​​Rc<T>

Rc<T>Multi-ownership is achieved through reference counting. The basic principle can be analogous to the TV set in the family:

  • When the first person enters the room to watch the TV, the TV "turns on", which means creating aRc<T>Example.
  • When others enter the room, they just need to "increase the reference count" (callRc::clone), the TV remains on.
  • When a certain audience leaves, the reference count decreases; the TV will be turned off only when the last audience leaves and the reference count drops to 0, and the corresponding data will be released.

useRc<T>, we do not need to specify which part has the data, but rely on reference count to ensure that as long as there are any part of the data in use, the data will not be cleaned.

Share data with Rc<T>

Here is a useRc<T>Example, this example demonstrates how to make two linked lists share the same child linked list.

We first define a linked list type, where each node usesRc<List>To hold the reference to the next node:

use std::rc::Rc;

enum List {
    Cons(i32, Rc&lt;List&gt;),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    // Create a shared linked list a: contains 5 and 10    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    
    println!("a Quote count = {}", Rc::strong_count(&amp;a)); // Output 1
    // Create a linked list b and share its ownership by cloning a    let b = Cons(3, Rc::clone(&amp;a));
    println!("a Quote count = {}", Rc::strong_count(&amp;a)); // Output 2
    {
        // Create a linked list c in a new scope, share a        let c = Cons(4, Rc::clone(&amp;a));
        println!("a Quote count = {}", Rc::strong_count(&amp;a)); // Output 3
        // c Reference count will automatically decrease when leaving scope    }
    
    println!("a Quote count = {}", Rc::strong_count(&amp;a)); // Output 2}

In this example, we first create aRc<List>Examplea. Then, by callingRc::clone(&a),WillaThe ownership of the link is passed to the link table separatelybandc. It should be noted thatRc::cloneIt just increased the reference count and did not make deep copy, so it was very efficient.

By callingRc::strong_count, we can view the changes in the reference count in the program. whencAfter leaving the scope, the count is automatically reduced by 1 until finally, when all references leave the scope, the reference count returns to zero and the data will be cleaned.

Rc<T> Limitations

AlthoughRc<T>A convenient multi-ownership mechanism is provided, but it can only be used in single-threaded scenarios. This is because the modification of reference count is not thread-safe. If you need to share data in a multi-threaded environment, you can use something likeArc<T>(Atomic Reference Count) type, which uses atomic operations internally to ensure multi-thread safety.

in addition,Rc<T>Only sharing of immutable references is allowed. If modifications are required on shared data, internal variability patterns must be used in combination, such asRc<T>andRefCell<T>Combined to check borrowing rules at runtime.

Summarize

  • Multiple ownership requirements: In some data structures, a value may be shared by multiple parts, and the traditional single ownership model cannot meet the needs.
  • Reference counting principleRc<T>Shared data is managed through reference counting, and the data is released only when the last reference leaves the scope.
  • Efficient cloning: CalledRc::cloneIt only increases the reference count and does not make deep copies, so it is very efficient.
  • limitRc<T>Suitable for single-threaded environments, and only immutable shared data is allowed; it should be considered when mutable sharing is requiredRefCell<T>or other solutions.

passRc<T>,Rust provides us with a simple and secure way to achieve multiple ownership, making the management of shared data more intuitive and efficient. I hope this blog can help you better understand and apply the multi-ownership mechanism in Rust, improving the flexibility and security of your code. Happy coding!

The above is personal experience. I hope you can give you a reference and I hope you can support me more.