I really want to use Rust for everything but borrowing restrictions have me confused

I really want to use Rust for all my projects but I keep running into borrowing issues with programs (like the one I explain below) that prevent me from doing so. If someone could briefly explain how to write the following program in an idiomatic way that would help me tremendously with my rust journey.

  • Have a Node struct that has two properties parents: Vec<Node> and children: Vec<Node>, these vectors contain references to other Nodes thereby achieving a tree-like structure.
  • Have a Tree struct that has a property nodes: HashMap<String, Nodes>.
  • On the Tree struct implement methods that modify the Nodes in the HashMap including properties of the Node itself.
pub struct Node {
    pub id: String,
    pub parents: Vec<String>,
    pub children: Vec<String>
}

impl Node {
  pub remove_child(id: String) {
    // Explain this
  }

  pub remove_parent(id: String) {
    // Explain this
  }
}
pub struct Tree {
    nodes: HashMap<String, Node>
}

impl Tree {
  pub fn remove_node(id: String) {
    // Explain this
  }
}

Example:
Let's say we want to remove a Node, the program should remove the node from the Hashmap on the Parent struct and for each of the remaining Nodes it should update the connections by reassigning parents/children.

How do I idiomatically write code for this?

The fact that you are trying to store children and parents means that you would ultimately need self-referential types. Rust doesn't allow those for good reasons. If you need bidirectionally-linked structured, use Rc and Weak.

1 Like

Thank you for your quick reply @H2CO3. Do you have any references that explain how to apply these to my problem in layman's terms?

Google "learning Rust with entirely too many linked lists". It will guide you through all the relevant concepts (and misconceptions).

Thanks so much.

The preferred data graph of Rust language is a picture of tree roots. The top of the root is where your main() function is located, then data references radiates downward preferably without any intersections.

Anywhere you are forming a new pointer pointing to something that already has an owning pointer, then the new pointer must be considered as a "borrow". This is tracked by the compiler. You can think this like borrowing money from the compiler; once your code goes out of scope then any borrowed pointers are returned to the compiler.

If you want a child node to have a pointer toward the parent that would be like to have tree root grow upward and form a circle; that is usually strongly discouraged in Rust.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.