I still use things like trees and DAGs, but usually avoid parent references (in the sense of a Weak
or whatnot) and instead if I need them, have something like a pool of nodes that I can look up by key. A pattern like that I used recently was to have short-lived borrowing traversal structs, something like
struct NodeView<'a> {
root: &'a Dag,
node: &'a Node,
}
And the node knew how to ask it's owning Dag
for it's parents, etc. [1]
Ultimately the best approach is going to come down to your use case and what you need the data structure for.
I don't know of any one-stop-shop, and a lot of it boils down to experience, but I'll throw some links out:
- A recent thread about understanding lifetime errors
- Indirect link to a walk-through about reborrows
- Laundry-list of RFCs if you're the kind of person who learns by reading a lot
- NLL is closest thing to a reference of what the borrow checker does internally; it's very long and technical and not always the easiest thing to connect back to your code
- Tree Borrows is a follow-up to stacked borrows
All the required operations were read-only; it would get trickier if that wasn't the case. ↩︎