我如何反序列化 Rust 中的引用树?
How can I deserialize a tree of references in Rust?
我昨天开始使用 Rust 参加今年的 Advent of Code。挑战 7 让您从文本文件中解析树结构。输入如下所示:
root -> child1, child2
child1 -> child3
child2
child3
此格式表示从 "root" 开始的树; "root"有两个children("child1"和"child2"),"child1"有一个child("child3")。 "child2"和"child3"没有children。该站点永远不会向您发送具有循环的输入。
解析没有问题,但我在构建树结构时遇到问题。
如果这是 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" 的映射,然后在第二步中填充对 "child programs" 的引用。如果您假设 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());
}
}
}
这不会构建:借用检查器非常不高兴我试图 double-borrow 从构建树的循环中可变。
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
当然,借用检查器是绝对正确的。这让我想到了一个问题:我正在尝试做的事情是否可行?
使用拥有的值而不是不可变的引用来为树建模更容易:一个节点拥有它的直接子节点。但是,由于问题7的objective是找到根节点,所以这可能不是最好的选择。
解决借用冲突问题的主要解决方案是使用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());
}
}
}
我发现在 Rust 中将某些树状对象实现为节点向量更容易。
每个节点都有一个id,就是它在vector中的位置,这个id作为一个指针。
struct Node {
parent: usize,
children: Vec<usize>,
}
根通常位于位置 0。
每当您通过将 Node
推到树向量上创建一个 Node
时,树向量 returns 其在插入之前的长度作为新 Node
的 ID。
如果您还需要删除节点,则必须稍微细化实现。
我昨天开始使用 Rust 参加今年的 Advent of Code。挑战 7 让您从文本文件中解析树结构。输入如下所示:
root -> child1, child2
child1 -> child3
child2
child3
此格式表示从 "root" 开始的树; "root"有两个children("child1"和"child2"),"child1"有一个child("child3")。 "child2"和"child3"没有children。该站点永远不会向您发送具有循环的输入。
解析没有问题,但我在构建树结构时遇到问题。
如果这是 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" 的映射,然后在第二步中填充对 "child programs" 的引用。如果您假设 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());
}
}
}
这不会构建:借用检查器非常不高兴我试图 double-borrow 从构建树的循环中可变。
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
当然,借用检查器是绝对正确的。这让我想到了一个问题:我正在尝试做的事情是否可行?
使用拥有的值而不是不可变的引用来为树建模更容易:一个节点拥有它的直接子节点。但是,由于问题7的objective是找到根节点,所以这可能不是最好的选择。
解决借用冲突问题的主要解决方案是使用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());
}
}
}
我发现在 Rust 中将某些树状对象实现为节点向量更容易。
每个节点都有一个id,就是它在vector中的位置,这个id作为一个指针。
struct Node {
parent: usize,
children: Vec<usize>,
}
根通常位于位置 0。
每当您通过将 Node
推到树向量上创建一个 Node
时,树向量 returns 其在插入之前的长度作为新 Node
的 ID。
如果您还需要删除节点,则必须稍微细化实现。