代码的基本思想是创建一个分层结构Context
,其中包含一些符号信息,并在Context
结构中向lambda提供一个Statement
以得到最终结果。如果需要,可以从父级派生子上下文:
use anyhow::Result; // 1.0.40
use std::{collections::HashMap, rc::Rc};
struct Context<'a> {
parent: Option<&'a mut Context<'a>>,
symbols: HashMap<String, i64>,
}
impl<'a> Context<'a> {
fn new() -> Self {
Context {
parent: None,
symbols: HashMap::new(),
}
}
fn derive(&'a mut self) -> Self {
Context {
parent: Some(self),
symbols: HashMap::new(),
}
}
}
#[derive(Clone)]
struct Statement {
execute: Rc<dyn Fn(&mut Context) -> Result<()>>,
}
struct Node {
cond: Statement,
stmts: Vec<Statement>,
}
impl Node {
fn get(&self) -> Statement {
let cond = self.cond.clone();
let stmts = self.stmts.clone();
Statement {
execute: Rc::new(move |ctx| {
(cond.execute)(ctx)?;
let mut cctx = ctx.derive();
for stmt in stmts {
(stmt.execute)(&mut cctx)?;
}
Ok(())
}),
}
}
}
当我编译这段代码时,出现错误:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:42:36
|
42 | let mut cctx = ctx.derive();
| ^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 40:30...
--> src/lib.rs:40:30
|
40 | execute: Rc::new(move |ctx| {
| ______________________________^
41 | | (cond.execute)(&mut ctx)?;
42 | | let mut cctx = ctx.derive();
43 | | for stmt in stmts {
... |
46 | | Ok(())
47 | | }),
| |_____________^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:42:32
|
42 | let mut cctx = ctx.derive();
| ^^^
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the body at 40:30...
--> src/lib.rs:40:30
|
40 | execute: Rc::new(move |ctx| {
| ______________________________^
41 | | (cond.execute)(&mut ctx)?;
42 | | let mut cctx = ctx.derive();
43 | | for stmt in stmts {
... |
46 | | Ok(())
47 | | }),
| |_____________^
note: ...so that the types are compatible
--> src/lib.rs:42:36
|
42 | let mut cctx = ctx.derive();
| ^^^^^^
= note: expected `&mut Context<'_>`
found `&mut Context<'_>`
我发现这个错误信息没用。如何修复此错误?
答案 0 :(得分:1)
根据您当前对 execute
的定义,Context
和 &mut
参数的生命周期因lifetime elision 规则而暗示不同,但您使用了 {{1} }} 在闭包中要求它们相同。
.derive()
您可以通过使用 higher-ranked trait bound 引入命名生命周期将它们链接在一起来解决此问题:
dyn Fn(&'a mut Context<'b>) -> Result<()>
但是,修复该问题并使 dyn for<'a> Fn(&'a mut Context<'a>) -> Result<()>
循环不消耗 for
(playground),您仍然会遇到生命周期问题:
stmts
由 Why does this mutable borrow live beyond its scope? 回答,简而言之,不要执行 error[E0499]: cannot borrow `*ctx` as mutable more than once at a time
--> src/lib.rs:42:32
|
40 | execute: Rc::new(move |ctx| {
| --- has type `&'1 mut Context<'1>`
41 | (cond.execute)(ctx)?;
| -------------------
| | |
| | first mutable borrow occurs here
| argument requires that `*ctx` is borrowed for `'1`
42 | let mut cctx = ctx.derive();
| ^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `cctx` as mutable more than once at a time
--> src/lib.rs:44:36
|
40 | execute: Rc::new(move |ctx| {
| --- has type `&'1 mut Context<'1>`
...
44 | (stmt.execute)(&mut cctx)?;
| ---------------^^^^^^^^^-
| | |
| | `cctx` was mutably borrowed here in the previous iteration of the loop
| argument requires that `cctx` is borrowed for `'1`
,它对试图使不同的生命周期全部对齐的借用检查器造成严重破坏,并且不可避免地会导致令人困惑的错误。
特别是使用可变引用受到更多限制,因此您可以通过使用不可变引用 &'a mut Context<'a>
来逃避它,但取决于您打算做什么,这可能不是一种选择。如果您需要可变性,您可能不得不通过 &'a Context<'a>
和 Rc
求助于共享所有权和内部可变性。
答案 1 :(得分:0)
所以问题与生命周期有关,但这还不是唯一的问题。
首先,每当我遇到闭包错误和奇怪的编译器消息时,我都会暂时用实际函数替换闭包,因为这往往会使某些错误消息更容易解析。
然后我尝试向代码中的许多其他部分添加显式生命周期参数:
use anyhow::Result; // 1.0.40
use std::{collections::HashMap, rc::Rc};
struct Context<'a> {
parent: Option<&'a mut Context<'a>>,
symbols: HashMap<String, i64>,
}
impl<'a> Context<'a> {
fn new() -> Self {
Context {
parent: None,
symbols: HashMap::new(),
}
}
fn derive(&'a mut self) -> Context<'a> {
Context {
parent: Some(self),
symbols: HashMap::new(),
}
}
}
#[derive(Clone)]
struct Statement<'a> {
execute: Rc<(dyn Fn(&'a mut Context<'a>) -> Result<()> + 'a)>,
}
struct Node<'a> {
cond: Statement<'a>,
stmts: Vec<Statement<'a>>,
}
impl<'a> Node<'a> {
fn get(&self) -> Statement<'a> {
let cond = self.cond.clone();
let stmts = self.stmts.clone();
Statement {
execute: Rc::new(move |ctx| {
(cond.execute)(ctx)?;
let mut cctx = ctx.derive();
for stmt in stmts {
(stmt.execute)(&mut cctx)?;
}
Ok(())
}),
}
}
}
这解决了生命周期问题,但还有很多其他问题:您慷慨地使用可变引用(可变借用),当然现在编译器抱怨您多次可变借用,因为您的生命周期需要引用保持足够长的时间!
这里的问题是,您需要同时保持对给定上下文的多个可变引用处于活动状态,这是 Rust 不允许的。
解决这个难题的方法不是尝试破解当前结构,而是重新考虑您是否真的需要所有这些可变引用,以及您是否也可以通过其他方式重新编写代码。
在其他面向对象的语言中,拥有大量(当然是可变的)引用来回指向的“对象海洋”是很常见的,但 Rust 不太喜欢那样。