簡體   English   中英

創建引用錯誤的遞歸列表“由於要求沖突,無法推斷 autoref 的適當生命周期”

[英]Creating a recursive list of references errors with "cannot infer an appropriate lifetime for autoref due to conflicting requirements"

該代碼的基本思想是創建一個包含符號的一些信息的分層結構Context ,並將一個Context提供給一個Statement結構中的 lambda 以獲得最終結果。 如果需要,可以從父級派生子級上下文:

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<'_>`

我發現這個錯誤信息是無用的。 我該如何解決這個錯誤?

根據您當前對execute的定義,由於生命周期省略規則, Context&mut參數的生命周期暗示不同,但您在閉包中使用.derive()要求它們相同。

dyn Fn(&'a mut Context<'b>) -> Result<()>

您可以通過使用更高級別的特征來解決此問題,該特征綁定到引入命名生命周期以將它們鏈接在一起:

dyn for<'a> Fn(&'a mut Context<'a>) -> Result<()>

但是,解決這個問題並使for循環消耗stmts ( playground ),您仍然會遇到終身問題:

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`

為什么這個可變借用超出了它的 scope? 簡而言之,不要做&'a mut Context<'a> ,它會對試圖使不同的生命周期對齊的借用檢查器造成嚴重破壞,並且不可避免地會導致令人困惑的錯誤。

特別是使用可變引用受到更多限制,因此您可以通過使用不可變引用&'a Context<'a>來擺脫它,但取決於您打算做什么,這可能不是一個選擇。 如果您需要可變性,您可能不得不通過RcRefCell訴諸共享所有權和內部可變性。

所以問題與生命周期有關,但這還不是唯一的問題。

首先,每當我遇到閉包錯誤和奇怪的編譯器消息時,我會暫時將閉包替換為實際的 function,因為這往往會使某些錯誤消息更容易解析。

然后我嘗試將顯式生命周期參數添加到代碼中的許多其他部分:

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 不允許的。

這個難題的解決方案不是試圖破解當前的結構,而是重新考慮是否真的需要所有這些可變引用,以及是否也可以以其他方式重新編寫代碼。

從其他面向 object 的語言來看,這種“對象之海”具有很多(當然是可變的)來回指向的引用是很常見的,但是 Rust 不太喜歡那樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM