add_child returns a borrow (&T), not an owned object (T), so a.add_child(h.add_child(i)) is simply a type error. You can’t give ownership of a borrowed object (that’s stealing!)
You have to write it like this:
h.add_child(i);
a.add_child(h);
If you made add_child return owned Self, then a.add_child(h.add_child(i)) would work, but then you’d have to do:
let h = h.add_child(i);
in order not to lose access to h, which is weird.
In languages without ownership semantics any way to access an object is equivalent. In Rust the way to achieve that is to wrap objects in Rc or Arc.
If instead of Node you use Rc<Node>, you’ll be able to return a clone of Rc<Node>, and all Rc references will be equally useful.
I would point out that graph data structures aren’t recommended in Rust, and modern processors can’t process them efficiently due to the large number of cache misses that incur.
Instead, you can use arenas[0] as an alternative to simulate graph data structures with less effort, without the downsides. Rather than allocating each individual node on the heap, you’d store each node in a single arena (vector).
In your case, each node would store the indexes of their sub-nodes, and this would allow you to share the same indexes with multiple nodes in the arena.
There are trade offs. I am looking for the boundaries.
I know that technique (arena).
I am hoping that I can trade some computational efficiency for expressiveness.
a.add_child(newNode(..).add_child()) is very expressive. But if I cannot I cannot!
I am used to writing in Perl or C++ where this is trivial. I am used to Rust limiting my options - I get why - but finding those boundaries is hard. (Maybe not so trivial in Perl…)