Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use Rc vs Box?

Tags:

I have the following code which uses both Rc and Box; what is the difference between those? Which one is better?

use std::rc::Rc;  fn main() {     let a = Box::new(1);     let a1 = &a;     let a2 = &a;     let b = Rc::new(1);     let b1 = b.clone();     let b2 = b.clone();      println!("{} {}", a1, a2);     println!("{} {}", b1, b2); } 

playground link

like image 324
o0omycomputero0o Avatar asked Mar 20 '18 05:03

o0omycomputero0o


People also ask

When to use Rc in Rust?

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.

Does rust use reference counting?

You have to enable multiple ownership explicitly by using the Rust type Rc<T> , which is an abbreviation for reference counting. The Rc<T> type keeps track of the number of references to a value to determine whether or not the value is still in use.


2 Answers

Rc provides shared ownership so by default its contents can't be mutated, while Box provides exclusive ownership and thus mutation is allowed:

use std::rc::Rc;  fn main() {     let mut a = Box::new(1);     let mut b = Rc::new(1);      *a = 2; // works     *b = 2; // doesn't } 

In addition Rc cannot be sent between threads, because it doesn't implement Send.

The bottom line is they are meant for different things: if you don't need shared access, use Box; otherwise, use Rc (or Arc for multi-threaded shared usage) and keep in mind you will be needing Cell or RefCell for internal mutability.

like image 184
ljedrz Avatar answered Oct 04 '22 21:10

ljedrz


Looking at the example given in the description, I think the real question here is "when to use Rc versus &Box" (note the ampersand).

Both Rc and &Box store the underlying data on the heap, neither can be sent across threads, and both allow immutable sharing (demonstrated by the aforementioned example). However, the biggest difference is that Rc gives you a shared (immutable) owned value while with &Box you get a shared (immutable) reference.

In the Rc case, the underlying data will be dropped (freed/deallocated) whenever the last owner (whether the original one or any cloned one) gets dropped – that's the idea of reference counting. In the &Box case, however, there is only one owner: any shared references to it will become invalid immediately after the owner gets out of scope.

Said differently, contrary to a Rc::clone(), binding a variable to a new &Box (let a2 = &a; in the example) will not make it live any longer than it would otherwise.

As a concrete example, the following is valid:

use std::rc::Rc;  fn main() {     let rc_clone;     {         let rc = Rc::new(1);         rc_clone = rc.clone();         // rc gets out of scope here but as a "shared owner", rc_clone         // keeps the underlying data alive.     }     println!("{}", rc_clone);  // Ok. } 

But this isn't:

fn main() {     let b_ref;     {         let b = Box::new(1);         b_ref = &b;         // b gets out of scope here and since it is the only owner,         // the underlying data gets dropped.     }     println!("{}", b_ref); // Compilation error: `b` does not live long enough. } 
like image 34
sitaktif Avatar answered Oct 04 '22 23:10

sitaktif