19

I'm looking at some code that uses

Rc<RefCell<SomeStruct>>

So I went out to read about the differences between Rc and RefCell:

Here is a recap of the reasons to choose Box, Rc, or RefCell:

Rc enables multiple owners of the same data; Box and RefCell have single owners.

Box allows immutable or mutable borrows checked at compile time; Rc allows only immutable borrows checked at compile time;

RefCell allows immutable or mutable borrows checked at runtime. Because RefCell allows mutable borrows checked at runtime, you can mutate the value inside the RefCell even when the RefCell is immutable.

So, Rc makes sure that SomeStruct is accessible by many people at the same time. But how do I access? I only see the get_mut method, which returns a mutable reference. But the text explained that "Rc allows only immutable borrows".

If it's possible to access Rc's object in mut and not mut way, why a RefCell is needed?

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150
  • "Rc allows only immutable borrows" – where is this quote from? It's not entirely accurate, so I'd like to correct it. The [official documentation](https://doc.rust-lang.org/std/rc/index.html) only states "You cannot generally obtain a mutable reference to something inside an `Rc`." This is appropriately qualified with "generally", so I think it's fine. – Sven Marnach May 25 '20 at 08:18
  • @SvenMarnach it's from here https://doc.rust-lang.org/book/ch15-05-interior-mutability.html – Guerlando OCs May 25 '20 at 21:18
  • Thanks. I'm not really sure whether this should be updated. While strictly speaking it's not true that you can only get shared borrows for the inner value of an `Rc`, it's true in most practical cases, so making this more correct may make it more confusing in other respects. – Sven Marnach May 26 '20 at 09:56
  • Does this answer your question? [Need holistic explanation about Rust's cell and reference counted types](https://stackoverflow.com/questions/45674479/need-holistic-explanation-about-rusts-cell-and-reference-counted-types) – trent May 26 '20 at 10:45

2 Answers2

25

So, Rc makes sure that SomeStruct is accessible by many people at the same time. But how do I access?

By dereferencing. If you have a variable x of type Rc<...>, you can access the inner value using *x. In many cases this happens implicitly; for example you can call methods on x simply with x.method(...).

I only see the get_mut method, which returns a mutable reference. But the text explained that "Rc allows only immutable borrows".

The get_mut() method is probably more recent than the explanation stating that Rc only allows immutable borrows. Moreover, it only returns a mutable borrow if there currently is only a single owner of the inner value, i.e. if you currently wouldn't need Rc in the first place. As soon as there are multiple owners, get_mut() will return None.

If it's possible to access Rc's object in mut and not mut way, why a RefCell is needed?

RefCell will allow you to get mutable access even when multiple owners exist, and even if you only hold a shared reference to the RefCell. It will dynamically check at runtime that only a single mutable reference exists at any given time, and it will panic if you request a second, concurrent one (or return and error for the try_borrow methods, respecitvely). This functionality is not offered by Rc.

So in summary, Rc gives you shared ownership. The innervalue has multiple owners, and reference counting makes sure the data stays alive as long as at least one owner still holds onto it. This is useful if your data doesn't have a clear single owner. RefCell gives you interior mutability, i.e. you can borrow the inner value dynamically at runtime, and modify it even with a shared reference. The combination Rc<RefCell<...>> gives you the combination of both – a value with multiple owners that can be borrowed mutably by any one of the owners.

For further details, you can read the relevant chapters of the Rust book:

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 1
    "it will panic if you request a second" nitpick: it doesn't have to, `try_borrow[_mut]` was added almost 4 years ago. – Masklinn May 25 '20 at 11:01
  • "RefCell will allow you to get mutable access even when multiple owners exist.". You mean `Rc>` right? `RefCell` alone only gives mutable access to one owner – Guerlando OCs May 25 '20 at 21:36
  • I've read a bit more. `RefCell` allows interior mutability. Since RefCell is a struct, I can have multiple immutable references to it, but still can mutate its contents by borrowing a mutable from it. So a RefCell allows for interior mutability, and can be passed around to anyone. Why `Rc` is needed? I thought `Rc` was the part that allowed to be passed to anyone. Can you give an example? – Guerlando OCs May 25 '20 at 22:33
  • @GuerlandoOCs I've expanded the answer a bit – hope this addresses your additional questions. – Sven Marnach May 26 '20 at 09:51
  • hm I think it's still not clear, but I don't think it's your fault. See: both RefCell and Rc permit multiple ownership. But RefCell does not have reference count. But I can't understand what's the difference between having and not having reference count. If a RefCell goes out of scope, it'll delete its object? I'm thinking that the only difference is the reference counting, but I don't see how it helps – Guerlando OCs May 28 '20 at 02:04
  • 2
    A `RefCell` per se only has a single owner. It allows you to obtain a mutable reference to the inner value even if you only have a shared reference to the `RefCell`. An `Rc`, on the other hand, allows shared ownership. The pointee is allocated on the heap and lives as long as there are still any references. These concepts are completely orthogonal to each other, and often complement each other well. – Sven Marnach May 28 '20 at 09:41
  • Thanks for the explanation, I still don't quite get what functionality will we lose if we just use RefCell without wrapping it buy Rc? – Amin Bashiri Nov 03 '22 at 01:25
  • 1
    @AminBashiri Without `Rc` or similar smart pointers, you can't have shared ownership. There will be a single owner, and the object will be dropped once the owner goes out of scope. – Sven Marnach Nov 03 '22 at 09:42
  • 1
    @AminBashiri I think reading [the chapter on ownership from the book Programming Rust](https://www.oreilly.com/library/view/programming-rust/9781491927274/ch04.html) will help you understand the concepts better. – Sven Marnach Nov 03 '22 at 09:45
1

If it's possible to access Rc's object in mut and not mut way, why a RefCell is needed?

  • Rc pointer allows you to have shared ownership. since ownership is shared, the value owned by Rc pointer is immutable

  • Refcell smart pointer represents single ownership over the data it holds, much like Box smart pointer. the difference is that box smart pointer enforces the borrowing rules at compile time, whereas refcell enforces the borrowing rules at run time.

If you combine them together, you can create a smart pointer which can have multiple owners, and some of the owners would be able to modify the value some cannot. A perfect use case is to create a doubly linked list in rust.

struct LinkedList<T>{
    head:Pointer<T>,
    tail:Pointer<T>
}

struct Node<T>{
    element:T,
    next:Pointer<T>,
    prev:Pointer<T>,
}
// we need multiple owners who can mutate the data
// it is Option because "end.next" would be None
type Pointer<T>=Option<Rc<RefCell<Node<T>>>>;

enter image description here

In the image "front" and "end" nodes will both point to the "middle" node and they can both mutate it. Imagine you need to insert a new node after "front", you will need to mutate "front.next". So in doubly linked you need multiple ownership and mutability power at the same time.

Yilmaz
  • 35,338
  • 10
  • 157
  • 202