我正在做Rust第7天的代码。我必须像这样解析一棵树:
a(10)
c(5) -> a, b
b(20)
这表示c
是以a
和b
作为其子女的根。
我通过解析每一行,创建一个对象,并按名称将它存储在一个哈希中来处理这个问题。如果它稍后显示为子项,如a
,我可以使用该哈希查找对象并将其作为子项应用。如果它在定义之前显示为子项,如b
,我可以创建一个部分版本并通过哈希更新它。以上就是这样的:
let mut np = NodeParser{
map: HashMap::new(),
root: None,
};
{
// This would be the result of parsing "a(10)".
{
let a = Node{
name: "a".to_string(),
weight: Some(10),
children: None
};
np.map.insert( a.name.clone(), a );
}
// This is the result of parsing "c(5) -> a, b".
// Note that it creates 'b' with incomplete data.
{
let b = Node{
name: "b".to_string(),
weight: None,
children: None
};
np.map.insert("b".to_string(), b);
let c = Node{
name: "c".to_string(),
weight: Some(5),
children: Some(vec![
*np.map.get("a").unwrap(),
// ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
*np.map.get("b").unwrap()
// ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
])
};
np.map.insert( c.name.clone(), c );
}
// Parsing "b(20)", it's already seen b, so it updates it.
// This also updates the entry in c.children. It avoids
// having to search all nodes for any with b as a child.
{
let mut b = np.map.get_mut( "b" ).unwrap();
b.weight = Some(20);
}
}
我可能想查找一个节点并查看其子节点。
// And if I wanted to look at the children of c...
let node = np.map.get("c").unwrap();
for child in node.children.unwrap() {
// ^^^^ cannot move out of borrowed content
println!("{:?}", child);
}
Rust不喜欢这个。它并不是说NodeParser.map
和Node.children
拥有一个节点。
error[E0507]: cannot move out of borrowed content
--> /Users/schwern/tmp/test.rs:46:21
|
46 | *np.map.get("a").unwrap(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
error[E0507]: cannot move out of borrowed content
--> /Users/schwern/tmp/test.rs:49:21
|
49 | *np.map.get("b").unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
它并不是说for
循环试图借用节点进行迭代,因为我已经从拥有它的NodeParser
借用了该节点。
error[E0507]: cannot move out of borrowed content
--> /Users/schwern/tmp/test.rs:68:18
|
68 | for child in node.children.unwrap() {
| ^^^^ cannot move out of borrowed content
我想我明白自己做错了什么,但我不确定如何做对。
我应该如何构建这个以使借款人高兴?由于必须链接NodeParser.map
和Node.children
的方式,因此无法复制。
Here is the code to test with。在实际代码中,Node
和NodeParser
都有实现和方法。
答案 0 :(得分:4)
一个选项是不安全的代码...但是如果你使用代码的出现来学习惯用的Rust而不仅仅是放弃它试图给你的所有安全性,我建议避免使用它。
另一种选择是引用Node
实例的计数,以便借用检查器很高兴并且编译器知道如何清理。 std::rc::Rc
类型为您执行此操作...基本上every call to clone()
just increments a reference count并返回一个新的Rc
实例。然后每次删除一个对象时,Drop
实现只会减少引用计数。
至于迭代.. for x in y
是for x in y.into_iter()
的语法糖。这是为了将children
的内容移出node
(the IntoIterator
trait中的通知,into_iter(self)
取得self
的所有权)。要解决此问题,您可以使用for x in &y
在迭代时请求引用。这基本上变成了for x in y.iter()
,它不会移动内容。
Here are these suggestions in action
use std::collections::HashMap;
use std::rc::Rc;
struct NodeParser {
map: HashMap<String, Rc<Node>>,
root: Option<Node>,
}
#[derive(Debug)]
struct Node {
name: String,
children: Option<Vec<Rc<Node>>>,
}
fn main() {
let mut np = NodeParser{
map: HashMap::new(),
root: None,
};
let a = Rc::new(Node{ name: "a".to_string(), children: None });
np.map.insert( a.name.clone(), a.clone() );
let b = Rc::new(Node{ name: "b".to_string(), children: None });
np.map.insert( b.name.clone(), b.clone() );
let c = Rc::new(Node{
name: "c".to_string(),
children: Some(vec![a, b])
});
np.map.insert( c.name.clone(), c.clone() );
let node = np.map.get("c").unwrap();
for child in &node.children {
println!("{:?}", child);
}
}
编辑:我将在此扩展我的评论。 You can use lifetimes here too if you want,但我担心终身解决方案会对MCVE产生影响,并且一旦应用于实际问题 OP就不会起作用(不仅仅是这个问题......其他问题) (实际上)。 Rust中的生命周期很棘手,像重新排序变量的实例化以允许终身解决方案可能会让人失望。我担心他们会遇到终身问题,因此即使它适用于MCVE,答案也不适合他们的实际情况。也许我会过度思考..