Rc<type> versus shared & references

Hello everyone

I am trying to learn Rust, reading "The Rust Book" and also reading Programming Rust book (both are excellent btw).

Can you please tell me when I should use reference counting Rc and when I should use built-in shared read-only Rust references &typevalue.

Example code (working code):

// first use Rc<> to create read only references

    let s: Rc<String> = Rc::new("shirataki".to_string());
    let t: Rc<String> = s.clone();
    let u: Rc<String> = s.clone();
    if t.as_ref().contains("rat") {
        println!("{:?}", t);
    }
    let zz = s;
    if zz.as_ref().contains("rat") {
        println!("zz {:?}", zz);
    }

    if t.as_ref().contains("rat") {
        println!("{:?}", t);
    }

// next we try the same but using & references

    let s1 = "shirataki".to_string();
    let t1 = &s1;
    let u1 = &s1;
    if t1.contains("rat") || u1.contains("shir") {
        println!("ref {:?} {:?} ", t1, u1);
    }

--

On the surface using Rc and using &typevalue yields the same result apart of less typing when using & references. Therefore I am guessing I should use the &references because Rust can figure things out at compile time. The Rc<> seems to be to use when Rust compiler refuses to compile my code, is this correct?

Here is what I have found in The Rust Book:

We use the Rc<T> type when we want to allocate some data on the heap for multiple parts of our program to read and we can’t determine at compile time which part will finish using the data last. If we knew which part would finish last, we could just make that part the data’s owner, and the normal ownership rules enforced at compile time would take effect.

Is above the complete answer to my question of Rc<> versus &ref or is there something else?

Thank you.

Rc/Arc is a standard approach to implement shared ownership, where it's not clear in the design/structure of your code that which component should exclusively own this data. For example if you need to share some data between two or more independent threads, it's best to wrap it under Arc and share it.

2 Likes

...and in a single-threaded setting, one example use case for Rc would be classic GUI frameworks, where you need some UI state to be accessible from multiple callbacks ("refresh" callback displaying the state, "button-clicked" callback updating the state, ...) without having a clearly designated owner for that state.

2 Likes

Yes. The borrow checker wants program's data ownership structured as a tree or directed acyclic graph. If you need parent-child relationships, or some items are created and destroyed at unpredictable times, then Rc is a solution for that.

4 Likes

Another perspective is that for newbies you probably shouldn't be putting references into data structures, because that tends to be hard, since then you have to convince the compiler that the data pointed to will outlive the data structure.

That said, it's usually nicer to put owned (but not shared) data into data structures, so I find myself only rarely using Rc and far more often using Box.

For any case where you want a pointer that isn't in a data structure, you probably want a reference, since it's simpler and has no overhead.

2 Likes

thank you everyone, its been very valuable to confirm my understanding.