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>returnsNoneif it has already been borrowed (exclusive/shared)borrow() -> Option<&T>returnsNoneif 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
RefStatebecause we would have to mutate the state with a shared reference. -
Here, wrapping the state in a
Cellwould solve the problem. BecauseCellallows 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
RefStatewould have to implement theCopytrait too -
Also, similar to
Cell,RefCellis not thread safe -
In practice, if we are implementing
RefCellourselves, we would return custom types, sayRefandRefMutfor theborrowandborrow_mutmethods. Now, we can have our own impls for theDrop,DerefandDerefMuttraits 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.