"借来的价值不够活跃"插入错误

时间:2016-11-29 22:40:43

标签: rust lifetime borrow-checker

我有一个简单的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 中有关此主题的部分。

1 个答案:

答案 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