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
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.
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.
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.
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. }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With