Trait tracing_rc::rc::traceable::Traceable [−][src]
pub trait Traceable {
fn visit_children(&self, visitor: &mut GcVisitor<'_>);
}
Expand description
Must be implemented for any value which will be stored inside of a Gc.
While this is implemented for many of Rust’s basic types, it’s not recommended that you store them in a Gc, as there is still a real cost to doing so. You’re probably better off using std::rc::Rc in cases where you know a type can’t participate in cycles.
Required methods
fn visit_children(&self, visitor: &mut GcVisitor<'_>)
fn visit_children(&self, visitor: &mut GcVisitor<'_>)
Visit the gc pointers owned by this type.
It is recommended that you simply call visit_children(visitor) on each value owned by the
implementor which may participate in a reference cycle. The default implementation for
Gc
will appropriately notify the collector when it is visited. You may also pass your
struct’s owned Gc
values directly to the visitor.
Impromper implementation of this trait will not cause undefined behavior, however, if you fail to report a value you may leak memory and if you report a value you don’t own (or report a value more than once), you may cause the collector to clean it up prematurely. Attemting to access a value which has been cleaned up will cause a panic, but will not cause undefined behavior.
Example
struct GraphNode<T: 'static> {
neighbors: Vec<Gc<RefCell<GraphNode<T>>>>,
data: T,
}
impl<T: 'static> Traceable for GraphNode<T> {
fn visit_children(&self, visitor: &mut GcVisitor) {
self.neighbors.visit_children(visitor);
}
}
empty_traceable!(NodeId);
struct Graph<T: 'static> {
nodes: HashMap<NodeId, Gc<RefCell<GraphNode<T>>>>,
}
impl<T: 'static> Traceable for Graph<T> {
fn visit_children(&self, visitor: &mut GcVisitor) {
self.nodes.visit_children(visitor);
}
}
// Usage:
{
{
let graph: Graph<usize> = build_graph();
operate_on_graph(&graph);
}
// None of the graph nodes will remain allocated after this call.
collect_full();
}
Examples of improper implementations
- You should not report Gcs owned by the inner contents of Gcs.
struct MyStruct {
ptr: Gc<MyStruct>,
other_ptr: Gc<MyStruct>,
}
impl Traceable for MyStruct {
fn visit_children(&self, visitor: &mut GcVisitor) {
// This is normal and ok.
self.ptr.visit_children(visitor);
// This is also acceptable
visitor.visit_node(&self.other_ptr);
// This will not cause undefined behavior, but it is wrong and may cause panics if
// it causes the collector to believe the node is dead, and the program later
// attempts to access the now dead value.
self.ptr.ptr.visit_children(visitor);
}
}
- You should not report a unique Gc instance twice.
struct MyStruct {
ptr: Gc<usize>,
}
impl Traceable for MyStruct {
fn visit_children(&self, visitor: &mut GcVisitor) {
// This is normal and ok.
visitor.visit_node(&self.ptr);
// This is wrong and may cause panics.
visitor.visit_node(&self.ptr);
}
}
- You should not report Gcs that are not owned by your object.
- It is acceptable skip reporting, although doing so will result in memory leaks.
thread_local! { static GLOBAL_PTR: Gc<usize> = Gc::new(10)}
struct MyStruct {
ptr: Gc<MyStruct>,
leaks: Gc<usize>,
}
impl Traceable for MyStruct {
fn visit_children(&self, visitor: &mut GcVisitor) {
// This is normal and ok.
visitor.visit_node(&self.ptr);
// Leaving this line commented out will leak, which is safe.
// Uncommenting it is safe and will allow leaks to be cleaned up.
// visitor(self.leaks.node());
// This is wrong and will cause GLOBAL_PTR to be cleaned up. If anything tries to
// access GLOBAL_PTR without checking if it is still alive, a panic will occur.
GLOBAL_PTR.with(|ptr| visitor.visit_node(ptr));
}
}