RefCell
-
Unlike other types where the borrow checking is done at compile time, RefCell allows us to check whether anyone else is mutating at run time
-
In essence, it allows safe dynamic borrowing
-
Useful in cases where data is not known at compile time: traversal of graphs and trees
-
Refcell is also implemented using
UnsafeCell
-
The structure has a way to keep track of how a value is borrowed
-
From the video, a rough implementation of the structure would look like this:
#![allow(unused)] fn main() { // uses this to keep track of how its borrowed enum RefState { Unshared, // noone has a reference Shared(usize), // there are n shared references Exclusive, // someone has an exclusive reference } struct RefCell<T> { value: UnsafeCell<T>, state: RefState, } }
-
The basic api should be something like:
borrow_mut() -> Option<&mut T>
returnsNone
if it has already been borrowed (exclusive/shared)borrow() -> Option<&T>
returnsNone
if there has already been an exclusive borrow
( there signatures are for understanding. the actual types will differ )
-
But now, we see that its not possble to implement this api directly with a simple
RefState
because we would have to mutate the state with a shared reference. -
Here, wrapping the state in a
Cell
would solve the problem. BecauseCell
allows us to mutate using a shared reference. Thus the structure becomes:#![allow(unused)] fn main() { struct RefCell<T> { value: UnsafeCell<T>, state: Cell<RefState>, } }
-
This means that
RefState
would have to implement theCopy
trait too -
Also, similar to
Cell
,RefCell
is not thread safe -
In practice, if we are implementing
RefCell
ourselves, we would return custom types, sayRef
andRefMut
for theborrow
andborrow_mut
methods. Now, we can have our own impls for theDrop
,Deref
andDerefMut
traits on these new types to keep our state updated. This allows us to gurantee safety for our implementation
Do check the video referenced above for the implementations and more explanation.