我有一个简单的trie实现,Edge
包含一个字符和另一个Node
的引用:
struct Edge<'a> {
ch: char,
to: &'a Node<'a>,
}
Node
包含边矢量:
pub struct Node<'a> {
edges: Vec<Edge<'a>>,
}
我试图实现将角色插入/获取节点的方法。我认为返回值应该是对Node
的引用:如果字符已经在其中一个边缘,那么我们直接返回现有的Node
;如果没有,我们将返回新创建的Node
。这是我遇到麻烦的地方:
impl<'a> Node<'a> {
fn get_or_create(&mut self, ch: char) -> &Node<'a> {
match self.edges.binary_search_by(|e| e.ch.cmp(&ch)) {
Ok(idx) => {
return &self.edges.get(idx).unwrap().to;
}
Err(idx) => {
let to = &Node { edges: Vec::new() };
let e = Edge { ch: ch, to: to };
self.edges.insert(idx, e);
return to;
}
}
}
}
据说to
的寿命不够长。
我很确定我所写的内容远非惯用的Rust。最初,当我在Node
中包含Edge
的引用时,我没有添加生命周期参数,并且被提示这样做,然后我不得不在任何地方添加它。然而,它看起来很奇怪。我想知道这样做的正确方法是什么?
也许我真正应该使用的是Edge
中的一些其他包装类型抽象来引用堆分配的Node
,例如Box
?我将仔细阅读 The Rust Programming Language 中有关此主题的部分。
答案 0 :(得分:2)
此数据结构无法按设计工作。红旗是以下句子:
我认为返回值应该是
Node
的引用:如果字符已经在其中一个边缘,那么我们直接返回现有的Node
;如果没有,我们将返回新创建的Node
。
代码不返回新创建的节点,它会尝试将引用返回给新创建的节点。如果对象存储在将超过引用的位置,则返回对对象的引用是安全的。否则,引用将最终指向堆栈中用于驻留对象的位置,从而导致使用时发生崩溃。像这样的错误经常是C和C ++崩溃的根源,正是Rust的借用检查器旨在防止的那种错误。
Rust使用函数和数据的lifetime参数跟踪参考生命周期。为了证明引用不会超过对象,Rust禁止引用的生命周期超出对象的生命周期。由于在函数末尾删除了新节点并且从函数返回了引用,因此对象的生命周期太短而且代码被正确拒绝为无效。
有几种可能的解决方法:
将Node
直接存储在Edge
内。这是shown to compile。
将&Node
更改为Rc<Node>
。这允许多个边缘共享单个节点的所有权,并自动释放。
在这两种情况下,将不再需要显式生命周期管理,并且所有权将“正常工作”。如果您了解C ++ 11,则Rc<>
大致相当于std::shared_ptr
。