1
votes

I started using Rust yesterday for this year's Advent of Code. Challenge 7 has you parse a tree structure from a text file. Inputs look like this:

root -> child1, child2
child1 -> child3
child2
child3

This format represents a tree that starts at "root"; "root" has two children ("child1" and "child2"), and "child1" has one child ("child3"). "child2" and "child3" have no children. The site will never send you input that has cycles.

Parsing is not a problem but I'm having trouble building the tree structure.

If this were C++, I'd write this:

struct Program {
    string name;
    vector<string> children;
};

struct ProgramNode {
    string& name;
    vector<ProgramNode*> children;

    ProgramNode(string& name);
};

vector<Program> programs = parse_programs();
unordered_map<string, ProgramNode> program_nodes;
for (Program& program : programs) {
    program_nodes.emplace(program.name, ProgramNode(program.name));
}

for (Program& program : programs) {
    ProgramNode& node = program_nodes.at(program.name);
    for (string& child : program.children) {
        node.children.push_back(&program_nodes.at(child));
    }
}

This builds a map of name to "program" in a first step, and in a second step it fills in the references to "child programs". This is safe if you assume that program_map doesn't outlive programs. Then, if you know the name of the root node, you can do ProgramNode& root = program_nodes.at(root_name) and play with your tree.

I'm trying to write the same thing in Rust, but I'm having issues with the borrow checker. So far I have something like this (with uninteresting details panic'd out): use std::collections::HashMap;

struct Program {
    name: String,
    children: Vec<String>,
}

struct ProgramNode<'a> {
    name: &'a str,
    children: Vec<&'a ProgramNode<'a>>,
}

impl<'a> ProgramNode<'a> {
    fn new(input: &'a Program) -> ProgramNode {
        panic!();
    }
}

fn parse_programs() -> Vec<Program> {
    panic!();
}

fn main() {
    let programs = parse_programs();

    let mut program_nodes = HashMap::new();
    for program in &programs {
        program_nodes.insert(&program.name, ProgramNode::new(&program));
    }

    for program in &programs {
        let mut program_node = program_nodes.get_mut(&program.name).unwrap();
        for child in &program.children {
            program_node
                .children
                .push(&program_nodes.get_mut(&child).unwrap());
        }
    }
}

This doesn't build: the borrow checker is Very Unhappy that I'm trying to double-borrow as mutable from the loop that builds the tree.

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:36:63
   |
36 |                 .push(&program_nodes.get_mut(&child).unwrap());
   |                        -------------------------------------- ^ temporary value dropped here while still borrowed
   |                        |
   |                        temporary value created here
...
39 | }
   | - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime

error[E0499]: cannot borrow `program_nodes` as mutable more than once at a time
  --> src/main.rs:32:32
   |
32 |         let mut program_node = program_nodes.get_mut(&program.name).unwrap();
   |                                ^^^^^^^^^^^^^ second mutable borrow occurs here
...
36 |                 .push(&program_nodes.get_mut(&child).unwrap());
   |                        ------------- first mutable borrow occurs here
...
39 | }
   | - first borrow ends here

error[E0499]: cannot borrow `program_nodes` as mutable more than once at a time
  --> src/main.rs:36:24
   |
32 |         let mut program_node = program_nodes.get_mut(&program.name).unwrap();
   |                                ------------- first mutable borrow occurs here
...
36 |                 .push(&program_nodes.get_mut(&child).unwrap());
   |                        ^^^^^^^^^^^^^ second mutable borrow occurs here
37 |         }
38 |     }
   |     - first borrow ends here

Of course, the borrow checker is absolutely correct. Which leads me to the question: is what I'm trying to do possible at all?

2
Related question about the same problem: stackoverflow.com/questions/47737084/…Francis Gagné

2 Answers

3
votes

It's easier to model a tree using owned values rather than immutable references: a node owns its immediate children. However, since the objective of problem 7 is to find the root node, it's probably not the best option.

The primary solution for solving the problem of conflicting borrows is to defer borrow checking to runtime by using RefCell.

use std::cell::RefCell;
use std::collections::HashMap;

struct Program {
    name: String,
    children: Vec<String>,
}

struct ProgramNode<'a> {
    name: &'a str,
    children: RefCell<Vec<&'a ProgramNode<'a>>>,
}

impl<'a> ProgramNode<'a> {
    fn new(input: &'a Program) -> ProgramNode { panic!(); }
}

fn parse_programs() -> Vec<Program> { panic!(); }

fn main() {
    let programs = parse_programs();

    let mut program_nodes = HashMap::new();
    for program in &programs {
        program_nodes.insert(&program.name, ProgramNode::new(&program));
    }

    for program in &programs {
        let mut program_node = program_nodes.get(&program.name).unwrap();
        for child in &program.children {
            program_node.children.borrow_mut().push(&program_nodes.get(&child).unwrap());
        }
    }
}
0
votes

I found it easier to implement certain tree-like objects in Rust as vectors of nodes.

Each node has an id, which is its position in the vector, and this id is used as a pointer.

struct Node {
    parent: usize,
    children: Vec<usize>,
}

Root is trivially at position 0.

Whenever you create a Node by pushing it onto the tree-vector, the tree-vector returns its length prior to the insertion as an id for the new Node.

If you also need to delete nodes, you have to refine the implementation a bit.