If I need to pass and update things around I always use Arc and I can't say I quite understand the point of Refcell. Can you please explain why would I need an Rc instead of Arc or are they completely different things and I misunderstood the entire thing wrong?
-
Arc
is a wrapper for shared ownership (i.e., used to solve lifetimes issues), by using atomic counters so that the "wrappee" is only dropped when the last owner is dropped; -
Rc
has the same functionality (shared ownership), except that the internal counters it uses do not have to be atomic. This means that:-
it cannot be used in multi-threaded scenarios (not
Send
norSync
); -
in a single threaded scenario it can be faster than
Arc
;
-
-
RefCell
is a wrapper used to solve cannot borrow mutably... issues, by providing Interior Mutability, which allows modifying a field of a struct through a shared reference&_
to that struct (mutation despite aliasing). This is possible by replacing the compile-time invariants that Rust provides with runtime checks that ensure / enforce these invariants (it uses counters too, to track the amount ofRef
andRefMut
borrows). It should be noted that the wayRefCell
handles misusage is bypanic!
king, which cannot cross thread boundaries, thus making it not usable in multi-threaded scenarios (it is notSync
); -
RwLock
is the multi-threaded equivalent ofRefCell
and its Interior Mutability: it uses atomic counters and instead ofpanic!
king it just waits for the counters to reach the right values, hence acting as a lock.-
Mutex
acts in a similar fashion (toRwLock
), but without the capacity to provide multiple readers. It can only provide single writer accesses (i.e., aRefMut
inRefCell
parlance), which are exclusive.
-
-
Finally, when the wrappee is just a plain integer, and you wish to use Interior Mutability with it, using
RefCell
/RwLock
is overkill: for increased performance, you can useAtomic{Integer}
, orCell<{integer}>
(faster but only for single-threaded usage).
Addendum
Regarding Rc
and Arc
, since they are used for shared ownership and thus aliasing, they do lend shared / aliased references (&_
) but do not lend unique / exclusive references (&mut _
).
- When you suspect that your shared ownership is actually unique, you can try to get a unique reference through the
get_mut
ormake_mut
methods (whose behaviors only differ when the hypothesis is wrong and there are multiple owners: if that is the case, the former fails, returningNone
, whereas the latterclone
s, hence guaranteeing indeed uniqueness)
That's why the only way to get both shared ownership (with its inherent aliasing) and mutation is through Interior Mutability, hence leading to two very common "wrapping patterns"; let's see if you can guess them:
-
Single-threaded
Click here to view the answer
Rc<RefCell<_>>
- (or
Rc<Cell<{integer}>>
)
- (or
-
Multi-threaded
Click here to view the answer
Arc<RwLock<_>>
- (or
Arc<Atomic{Integer}>
)
- (or
Short answer for your second question: Rc
is just a micro-optimization to avoid the perf impact of coherent memory when you're only in one thread. You never need to use it.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.