创建引用错误的递归列表,“由于需求冲突,无法推断 autoref 的适当生命周期”

时间:2021-06-09 15:03:41

标签: rust lifetime

代码的基本思想是创建一个分层结构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<'_>`

我发现这个错误信息没用。如何修复此错误?

2 个答案:

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

所以问题与生命周期有关,但这还不是唯一的问题。

首先,每当我遇到闭包错误和奇怪的编译器消息时,我都会暂时用实际函数替换闭包,因为这往往会使某些错误消息更容易解析。

然后我尝试向代码中的许多其他部分添加显式生命周期参数:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0f5bb5010ea26cff3f99237b4439eba2

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 不太喜欢那样。