reify-graph 0.1.1

Safe Rc/Arc graph reification and reconstruction
Documentation

Convert Rc<RefCell<T>> and Arc<Mutex<T>> pointer graphs into a flat, inspectable, serializable form, and back.

Pointer-shaped data (trees with shared subtrees, DAGs, cyclic graphs) is awkward to serialize, log, or transform: walking it naively either duplicates shared nodes or loops forever on cycles. This crate gives you a small, non-invasive API to:

  • Reify: turn a pointer graph rooted at an Rc<RefCell<T>> (or Arc<Mutex<T>>) into a flat [ReifiedGraph] of nodes and edges, keyed by pointer identity, detecting cycles and preserving sharing.
  • Reflect: rebuild the original pointer graph from a [ReifiedGraph], restoring the same sharing topology (one allocation per node id, even when many edges point to it).

With the default serde feature enabled, [ReifiedGraph] is Serialize + Deserialize, which gives you JSON / postcard / etc. serialization of arbitrary cyclic data essentially for free.

When to use it

  • You want to dump or log a graph for debugging (an AST with shared sub-expressions, a circuit netlist, a scene graph).
  • You want to send pointer-shaped state across a process boundary.
  • You want to apply a structural transform (renumbering, GC, deduping) that's awkward on the live Rc graph but easy on a flat node+edge form.

Design choices

  • Node identity comes from [Rc::as_ptr] (or Arc::as_ptr) cast to usize. No traits or wrappers required on your T.
  • Children are extracted by a closure you supply, so the library never guesses at the structure of your type.
  • Node data is cloned into the [ReifiedGraph] (use Arc<Inner> inside T if cloning is expensive).
  • Reconstruction in [reflect_graph] preserves sharing exactly: each [NodeId] becomes one [Rc] and is re-pointed everywhere it was referenced.

See the docs/phase2-graph-reification.md design note, and the serialize_graph example for an end-to-end serde_json round trip.

Examples

Round-trip a small Rc<RefCell<_>> tree through the flat form:

use reify_graph::{reify_graph, reflect_graph};
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Clone, Debug)]
struct Node {
    value: i32,
    children: Vec<Rc<RefCell<Node>>>,
}

let leaf = Rc::new(RefCell::new(Node { value: 1, children: vec![] }));
let root = Rc::new(RefCell::new(Node {
    value: 0,
    children: vec![leaf.clone()],
}));

// Flatten: 2 nodes, 1 edge
let graph = reify_graph(root.clone(), |n| n.children.clone());
assert_eq!(graph.nodes.len(), 2);
assert_eq!(graph.edges.len(), 1);

// Rebuild, restoring the original sharing
let reconstructed = reflect_graph(graph, |n, kids| n.children = kids);
assert_eq!(reconstructed.borrow().value, 0);
assert_eq!(reconstructed.borrow().children[0].borrow().value, 1);

For Arc<Mutex<T>> graphs (the same pattern, thread-safe), see the [arc] module's reify_graph_arc and reflect_graph_arc.