If you're building graphs because you want to actually use a graph for something else and not because you're trying to learn how to build graph data-structures, I highly recommend using petgraph, it has a bunch of graph types as well as a pile of useful algorithms on graphs such as walking the graph, finding all the cycles, computing a topological ordering, and many more.
That said, if you're trying to figure out how to build your own graph data-structures, one other option to consider is to use arenas, which lets you create &'ctx MyType everytime you need a new MyType in your self-referential datastructure, though they have some downsides such as you can't move the arenas, and you can't deallocate anything in your datastructure until you drop the arenas which deallocates everything at once.
This code on the playground
use std::cell::RefCell;
use typed_arena::Arena;
/// you'll need a context struct that holds all your arenas,
/// all the rest of your graph will borrow from the context struct.
#[derive(Default)]
struct Context<'ctx> {
graph_arena: Arena<Graph<'ctx>>,
node_arena: Arena<Node<'ctx>>,
edge_arena: Arena<Edge<'ctx>>,
}
struct Node<'ctx> {
outgoing_edges: RefCell<Vec<&'ctx Edge<'ctx>>>,
incoming_edges: RefCell<Vec<&'ctx Edge<'ctx>>>,
data: RefCell<String>,
}
struct Edge<'ctx> {
start_node: &'ctx Node<'ctx>,
end_node: &'ctx Node<'ctx>,
data: RefCell<String>,
}
struct Graph<'ctx> {
nodes: RefCell<Vec<&'ctx Node<'ctx>>>,
ctx: &'ctx Context<'ctx>,
}
impl<'ctx> Graph<'ctx> {
fn new(ctx: &'ctx Context<'ctx>) -> &'ctx Graph<'ctx> {
ctx.graph_arena.alloc(Graph {
nodes: RefCell::new(vec![]),
ctx,
})
}
fn add_node(&self, data: String) -> &'ctx Node<'ctx> {
let node = self.ctx.node_arena.alloc(Node {
outgoing_edges: RefCell::new(vec![]),
incoming_edges: RefCell::new(vec![]),
data: RefCell::new(data),
});
self.nodes.borrow_mut().push(node);
node
}
fn add_edge(
&self,
start_node: &'ctx Node<'ctx>,
end_node: &'ctx Node<'ctx>,
data: String,
) -> &'ctx Edge<'ctx> {
let edge = self.ctx.edge_arena.alloc(Edge {
start_node,
end_node,
data: RefCell::new(data),
});
start_node.outgoing_edges.borrow_mut().push(edge);
end_node.incoming_edges.borrow_mut().push(edge);
edge
}
// we can't just use #[derive(Debug)] since that would be infinite recursion
fn dump(&self) {
println!("Graph:");
for Node {
incoming_edges: _,
outgoing_edges,
data,
} in self.nodes.borrow().iter()
{
println!("Node: {:?}", data.borrow());
for Edge {
start_node,
end_node,
data: edge_data,
} in outgoing_edges.borrow().iter()
{
println!(" Edge: {:?}", edge_data.borrow());
println!(
" {:?} -> {:?}",
start_node.data.borrow(),
end_node.data.borrow(),
);
}
}
}
}
fn demo<'ctx>(ctx: &'ctx Context<'ctx>) {
let graph = Graph::new(ctx);
let foo = graph.add_node("foo".into());
let bar = graph.add_node("bar".into());
let baz = graph.add_node("baz".into());
graph.add_edge(foo, foo, "self-loop".into());
graph.add_edge(foo, bar, "foo to bar".into());
graph.add_edge(baz, bar, "baz to bar".into());
graph.add_edge(bar, baz, "bar to baz".into());
graph.dump();
}
fn main() {
let ctx = Context::default();
demo(&ctx);
}