如何在 Rust 中修复“.. 在循环的前一次迭代中在这里被可变地借用”?

4nt*_*ine 10 ownership rust

我必须迭代键,通过键在 HashMap 中找到值,可能在找到的结构中做一些繁重的计算作为一个值(懒惰 => 改变结构)并在 Rust 中缓存返回它。

我收到以下错误消息:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:25:26
   |
23 |     fn it(&mut self) -> Option<&Box<Calculation>> {
   |           - let's call the lifetime of this reference `'1`
24 |         for key in vec!["1","2","3"] {
25 |             let result = self.find(&key.to_owned());
   |                          ^^^^ `*self` was mutably borrowed here in the previous iteration of the loop
...
28 |                 return result
   |                        ------ returning this value requires that `*self` is borrowed for `'1`
Run Code Online (Sandbox Code Playgroud)

这是操场上的代码

use std::collections::HashMap;

struct Calculation {
    value: Option<i32>
}

struct Struct {
    items: HashMap<String, Box<Calculation>> // cache
}

impl Struct {
    fn find(&mut self, key: &String) -> Option<&Box<Calculation>> {
        None // find, create, and/or calculate items
    }

    fn it(&mut self) -> Option<&Box<Calculation>> {
        for key in vec!["1","2","3"] {
            let result = self.find(&key.to_owned());
            if result.is_some() {
                return result
            }
        }
        None
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 我无法避免循环,因为我必须检查多个键
  • 我必须使它可变(self和结构),因为可能的计算会改变它

关于如何更改设计的任何建议(因为 Rust 强制以一种有意义的不同方式思考)或解决它?

附注。代码还有一些其他问题,但让我们先拆分问题并解决这个问题。

Bro*_*ind 5

可能不是最干净的方法,但它可以编译。这个想法是不存储在临时结果中找到的值,以避免别名:如果存储结果,self则保持借用。

impl Struct {

    fn find(&mut self, key: &String) -> Option<&Box<Calculation>> {
        None
    }

    fn it(&mut self) -> Option<&Box<Calculation>> {
        for key in vec!["1","2","3"] {
            if self.find(&key.to_owned()).is_some() {
                return self.find(&key.to_owned());
            }
        }
        None
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我同意你的看法,可能会更好。另一方面,对于您的特定情况,“find”并不那么昂贵,因为它只是在哈希表中查找,并且您知道该值存在。如果我正确理解你的意图,那么繁重的计算无论如何都会完成一次,所以与此相比,这个双重调用可能相当小。我会尝试想出更好的方法来做到这一点。 (2认同)

Kor*_*nel 5

您不能使用独占访问进行缓存。您不能将 Rust 引用视为通用指针(顺便说一句:&String并且&Box<T>是双重间接引用,并且在 Rust 中非常单调。使用&str&T用于临时借用)。

&mut self不仅意味着可变,而且是独占和可变的,因此您的缓存仅支持返回一项,因为self只要它存在,它返回的引用就必须保持“锁定”。

您需要说服借用检查器find返回的东西不会在您下次调用时突然消失。目前没有这样的保证,因为接口不会阻止你调用 eg items.clear()(借用检查器检查什么函数的接口允许,而不是什么函数实际执行)。

您可以通过使用Rc或使用实现内存池/arena的板条箱来做到这一点。

struct Struct {
   items: HashMap<String, Rc<Calculation>>,
}

fn find(&mut self, key: &str) -> Rc<Calculation> 
Run Code Online (Sandbox Code Playgroud)

这样,如果您克隆Rc,它将在需要时一直存在,而与缓存无关。

您还可以通过内部可变性使其更好。

struct Struct {
   items: RefCell<HashMap<…
}
Run Code Online (Sandbox Code Playgroud)

这将允许您的记忆find方法使用共享借用而不是独占借用:

fn find(&self, key: &str) -> …
Run Code Online (Sandbox Code Playgroud)

这对于方法的调用者来说更容易使用。