循环变量的寿命不够长

时间:2017-07-12 10:27:28

标签: rust

过去几天我一直在玩Rust, 我仍然在努力处理内存管理(数字)。

我写了一个simple project,它从lexing /解析文本文件中创建了一个结构层次结构(“keywords”)。

关键字的定义如下:

pub struct Keyword<'a> {
    name: String,
    code: u32,
    parent: Option<&'a Keyword<'a>>,
}

这是我对这个C结构的等价物:

typedef struct Keyword Keyword;

struct Keyword {
    char* name;
    unsigned int code;
    Keyword* parent;
};

层次结构只是关键字的容器,定义如下:

pub struct KeywordHierarchy<'a> {
    keywords: Vec<Box<Keyword<'a>>>,
}

impl<'a> KeywordHierarchy<'a> {
    fn add_keyword(&mut self, keyword: Box<Keyword<'a>>) {
        self.keywords.push(keyword);
    }
}

parse函数(是完整解析器的存根)中,我重新创建了在我的代码中产生生命周期错误的条件。

fn parse<'a>() -> Result<KeywordHierarchy<'a>, String> {

    let mut hierarchy = KeywordHierarchy { keywords: Vec::new() };

    let mut first_if = true;
    let mut second_if = false;

    while true {

        if first_if {

            // All good here.

            let root_keyword = Keyword {
                name: String::from("ROOT"),
                code: 0,
                parent: None,
            };
            hierarchy.add_keyword(Box::new(root_keyword));

            first_if = false;
            second_if = true;
        }

        if second_if {

            // Hierarchy might have expired here?

            // Find parent
            match hierarchy.keywords.iter().find(|p| p.code == 0) {
                None => return Err(String::from("No such parent")),
                Some(parent) => {

                    // If parent is found, create a child
                    let child = Keyword {
                        name: String::from("CHILD"),
                        code: 1,
                        parent: Some(&parent),
                    };
                    hierarchy.add_keyword(Box::new(child));
                }
            }

            second_if = false;
        }

        if !first_if && !second_if {
            break;
        }
    }

    Ok(hierarchy)

}

有一个while循环遍历词法分析器标记。

在第一个if中,我将 ROOT 关键字添加到层次结构中,这是唯一一个没有父级的关键字,一切都按预期顺利进行。

在第二个if中,我解析了children关键字,并在调用KeywordHierarchy.add_keyword()时出现了生命周期错误。

hierarchy.keywords` does not live long enough

你们可以推荐一种惯用的方法来解决这个问题吗?

干杯。

P.S。点击游乐场的here

1 个答案:

答案 0 :(得分:3)

我可以在您的设计中看到的直接问题是您的循环将修改hierarchy.keywords向量(在first_if块中),但它也借用hierarchy.keywords中的元素向量(在second_if块中)。

这是有问题的,因为修改向量可能会导致其后备缓冲区被重新分配,如果允许,它将使向量的所有现有借用无效。 (因此不允许这样做。)

您是否考虑使用arena来保留关键字而不是Vec?阿里纳斯的设计是为了让你可以在其中分配新的东西,同时仍然可以对先前在竞技场内分配的元素进行优秀借阅。

更新:以下是您的代码的修订版本,其中说明了使用竞技场(在这种情况下为rustc_arena::TypedArena,但这只是为了让我可以在play.rust-lang.org服务上运行)一个Vec<&Keyword>来处理查找。

https://play.rust-lang.org/?gist=fc6d81cb7efa7e4f32c481ab6482e587&version=nightly&backtrace=0

代码的关键部分是:

首先:KeywordHierarchy现在与vec一起举办竞技场:

pub struct KeywordHierarchy<'a> {
    keywords: Vec<&'a Keyword<'a>>,
    kw_arena: &'a TypedArena<Keyword<'a>>,
}

第二:添加关键字现在可以在竞技场中分配点,并在vec中存储对该点的引用:

fn add_keyword(&mut self, keyword: Keyword<'a>) {
    let kw = self.kw_arena.alloc(keyword);
    self.keywords.push(kw);
}

第三:fn parse函数现在将竞技场(引用)作为输入,因为我们需要竞技场超过fn parse的堆栈帧:

fn parse<'a>(arena: &'a TypedArena<Keyword<'a>>) -> Result<KeywordHierarchy<'a>, String> {
    ...

第四:为了避免借用hierarchy作为可变的借用检查器问题,同时迭代它,我将hierarchy修改移到了查找父代match之外:

        // Find parent
        let parent = match hierarchy.keywords.iter().find(|p| p.code == 0) {
            None => return Err(String::from("No such parent")),
            Some(parent) => *parent, // (this deref is important)
        };
        // If parent is found, create a child
        let child = Keyword {
            name: String::from("CHILD"),
            code: 1,
            parent: Some(parent),
        };
        hierarchy.add_keyword(child);
        second_if = false;