我过去几天一直在和Rust一起玩,
而且我仍在努力进行内存管理(图)。
我写了一个simple project,它通过词法分析/解析文本文件来创建结构的层次结构(“关键字”)。
关键字的定义如下:
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
函数(是完整解析器的 stub )中,我重新创建了在代码中产生生命周期错误的条件。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)
}
有一个通过词法分析器 token 的
while
循环。在第一个
if
中,我将 ROOT 关键字添加到层次结构中,这是唯一没有父级的关键字,一切按预期进行。在第二个
if
中,我解析了children关键字,并且在调用KeywordHierarchy.add_keyword()
时遇到了生命周期错误。hierarchy.keywords` does not live long enough
你们能推荐一种惯用的方法来解决此问题吗?
干杯。
P.S.单击here进入游乐场
最佳答案
我在您的设计中看到的直接问题是您的循环将修改hierarchy.keywords
向量(在first_if
块中),但是它也从hierarchy.keywords
向量(在second_if
块中)中借用了元素。
这是有问题的,因为修改向量可能会导致其后备缓冲区被重新分配,如果允许,则会使向量的所有现有借用无效。 (因此,这是不允许的。)
您是否考虑过使用arena代替Vec
来保留关键字? Arenas的设计使您可以在其中分配新事物,同时仍可以向以前在竞技场中分配的元素进行大量借用。
更新:这是您代码的修订版,说明了使用竞技场(本例中为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;