我昨天开始使用Rust来做今年的Code of Advent。挑战7让您从文本文件中解析树结构。输入看起来像这样:
root -> child1, child2
child1 -> child3
child2
child3
此格式表示以“root”开头的树; “root”有两个孩子(“child1”和“child2”),“child1”有一个孩子(“child3”)。 “child2”和“child3”没有孩子。该网站永远不会向您发送具有周期的输入。
解析不是问题,但我在构建树结构时遇到了问题。
如果这是C ++,我会写这个:
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));
}
}
这将在第一步中将名称的地图构建为“程序”,在第二步中,它将填写对“子程序”的引用。如果您认为program_map
不会超过programs
,则这是安全的。然后,如果您知道根节点的名称,则可以执行ProgramNode& root = program_nodes.at(root_name)
并使用您的树。
我正在尝试在Rust中编写相同的内容,但我遇到了借用检查程序的问题。到目前为止,我有这样的事情(有无趣的细节panic
'):
使用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());
}
}
}
这不构建:借用检查器非常不高兴,我正在尝试从构建树的循环中双重借用。
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
当然,借阅检查是绝对正确的。这引出了我的问题:我正在尝试做什么?
答案 0 :(得分:2)
使用拥有值而不是不可变引用来建模树更容易:一个节点拥有其直接子节点。但是,由于问题7的目标是找到根节点,它可能不是最好的选择。
解决冲突问题的主要解决方案是使用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());
}
}
}
答案 1 :(得分:0)
我发现在Rust中实现某些类似树的对象更容易作为节点的向量。
每个节点都有一个id,它是它在向量中的位置,这个id用作指针。
struct Node {
parent: usize,
children: Vec<usize>,
}
Root很容易就在0号位置。
每当您通过将Node
推到树矢量上来创建Node
时,树矢量会在插入之前将其长度作为新{{1}}的ID返回。
如果您还需要删除节点,则必须稍微改进实现。