rust - 如何正确解决结构中的生命周期推断?
问题描述
Rust游乐场代码在这里。
我有一个具有生命周期的 Token 结构'tok
,而扫描器具有生命周期'lexer
。我在另一个结构解析器中使用它们,然后我遇到了一个问题:
pub struct Token<'tok> {
pub value: Cow<'tok, str>,
pub line: usize,
}
pub struct Scanner {
pub source: Vec<char>,
pub current: usize,
pub line: usize,
}
pub struct Parser<'lexer> {
pub curr: &'lexer Token<'lexer>,
pub prev: &'lexer Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
self.prev = self.curr;
self.curr = &self.scanner.next(); // cannot inference lifetime
}
}
我认为问题是 Token 有生命周期'tok
,而借用检查器不知道两者之间的关系'tok
,'lexer
因此它无法推断出正确的生命周期。
但是,我可以通过将其修改为更新的代码来避免该问题:
pub struct Parser<'lexer> {
pub curr: Token<'lexer>,
pub prev: Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
let prev = std::mem::replace(&mut self.curr, self.scanner.next());
self.prev = prev;
}
}
并且使用 Token 生成的next()
是静态的:
impl Scanner {
pub fn next(&mut self) -> Token<'static> {
Token {
value: Cow::from(""),
line: 0,
}
}
}
它确实可以编译,但我认为这并不理想,因为所有标记都从扫描仪克隆到解析器(它们不再是引用)并一直存在到解析器生命的尽头。所以内存使用量翻了一番。有没有更合适的方法来处理这个问题?
解决方案
实际上,您的代码结构对于处理借用检查器并不理想,原因如下:
Token
struct 应该被拥有,struct 本身不需要任何分配(因为令牌是拥有的,所以需要一些 indrection 来允许 prev <-> next 切换)- 任何人都不
Parser
应该Lexer
拥有基础数据,因此很容易绑定适当的生命周期 - 在我们的例子
Vec<Char>
中不是一个友好的类型(我们不需要拥有数据,这将更难让编译器理解生命周期),相反我们将使用 &'str 但你可以重现确切的带有 &[char] 的行为)
这是一个编译得很好的例子
pub struct Token<'source> {
pub value: Cow<'source, str>,
pub line: usize,
}
pub struct Scanner<'source> {
pub source: &'source str,
pub current: usize,
pub line: usize,
}
pub struct Parser<'source> {
pub curr: Option<Token<'source>>,
pub prev: Option<Token<'source>>,
scanner: Scanner<'source>,
}
impl <'source>Scanner<'source> {
pub fn next(&'source /* DONT Forget to bound 'source to `self` */ self) -> Token<'source> {
Token {
value: Cow::from(self.source), /* `self.source` is bound to `'source` so the compiler understand that the token lifetime is the same than the source's one */
line: 0,
}
}
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&'lexer mut self) {
self.prev = self.curr.take();
self.curr = Some(self.scanner.next());
}
}
推荐阅读
- asp.net - MVC如何在动作参数中传递两个输入文本
- python - Python3:在不中断导入的情况下将包含子类/超类的自包含 git 子模块导入更大的项目结构
- r - 试图抓取到 csv 文件
- php - 如何在php中关闭模态后删除消息
- c++ - 向量
损坏的 C++ - firebase - Firebase 和世博会。部署 Expo 应用程序。隐藏键
- javascript - 带有嵌入式迷你图的 Highcharts 工具提示
- c - 通过 STM32 闪存获得 CRC-32 并与其他 CRC-32 工具保持一致
- c - 在 C 程序中使用 printf 打印时添加到我的 char 字符串中的未定义行为(额外符号)
- css - 带有不同尺寸卡片的引导轮播