Rust-GC note
The gist about design
magic_gc_XXX
seems to be global & static.
What's diff between mark, trace and add_root?
add_root: ref-counted, which happens during object acquisition will be dynamically recorded.
mark: only used during GC period?
trace: ... it will be called recursively, it is a trait of obj. so trace is to tract, and mark on the traced stuff? Where is the mark? How the mark is implemented as a ds?
If some T
implements Trace
, it can be traced? What is the semantics here?
GcBox is the internal ds hold a GCed value. It has the root reqs on it. "how to understand rooting as a req?" Why the _roots
is implemented as a Cell
.
Cell
andRefCell
are shareable mutable container.Cell<T>
providesget
andset
methods that change the interior.Cell<T>
is only compatible withCopy
able value. For other type,RefCell
with mutex has to be used.
RefCell
's life time is tracked dynamically, in contrast to the static tracking of Rust's native reference types.
What is NonZero
? malloc_gc_allocate
would return such a wrapped mutable raw pointer to a GcBox type.
Maybe the best way to understand Rust is to draw something
GcBox { roots; value }
| |
v \---> T
Heap Cell
|
v
usize
The value of roots
is accesses with get
and set
.
GC is another layer of container. Is is another wrapped by mysterious NonZero
but containing a non-mutable raw pointer pointing to a GcBox
.
When new
, the GcBox will be unrooted first. It is something stored internally. The reference to GcBox
can also be exposed to outside world with inner
method.
While the interesting thing is to provide the Trace
trait to Gc
. It is like to say, anything (T
) can be traced can be GCed as long as we can trace the Gc<T>
. The methods are mostly delegated to the inner GcBox
.
We will also implement Clone
and Deref
to Gc<T>
.
Clone
trait is for types that cannot be "implicitly copied". When the type is not so simple, and it is allocated and has finalizers (i.e. they do not contain owned boxed or implementDrop
), it can't be implicitly copied.
Deref
's semantic is to take a value from a pointer-like stuff.
GcCell
is mutable. It doesn't use T
directly, but wrapped with another layer of RefCell
.
borrow
lasts until the returned ref Ref
exits its scope. borrow_mut
is mutable and unique. Ref
wraps a borrowed reference to a value in a RefCell
box. borrow_state
can be used to do a inspection. If it already borrowed mutably, it is already rooted somewhere else so we don't have to trace again. If not, we will trace its internal value.
GcCellRefMut
acts as a RAII guard. It provides access to inner value while ensuring that the data isn't collected incorrectly. It has both a ref
and a box
. So why two sources?
For Deref
and DerefMut
, the ref one is used.
For Drop, both the box and the ref is unrooted.
A remarkable point is that it is explicitly notated a lifetime 'a
why?
RefMut
is a wrapper type for a mutably borrowed value from a RefCell<T>
GcCellRefMut { _box; _ref }
| |
/--/ \---\
| |
v v
GcBox RefMut
| |
v /
RefCell /
| /
v /
value <------/
Here is something to clarify: mut
, ref
, and cell
.
For the native mut
used by Rust compiler, it is a static checking mark to ensure that the mutability is declared. So it is easier to check other operations involving ownership.
While for ref
, it is mostly a library function, providing some more flexible &
semantics. It is from core::cell
. cell
is a data structure. Sharing is implemented by Rc
, while mutable sharing is provided by RefCell
. I don't know clearly its relationship with the more builtin stuff however.