不能借用 `self.blocks` 作为可变的,因为它也被借用为不可变的

问题描述

如果 btreemap 的键小于一个值,我想删除 btreemap 的键和值:

fn update_confirmed_height(&mut self,new_confirmed_height: u32) {
    assert!(new_confirmed_height >= self.confirmed_height);
    assert!(new_confirmed_height <= self.current_height);

    for height in self.blocks.keys() {
        if height < &new_confirmed_height {
            self.blocks.remove(&height);
        }
    }
}

但是self.blocks.remove(&height)报错: cannot borrow self.blocks as mutable because it is also borrowed as immutable

解决方法

据我所知,有三种解决方案。

  1. 如果您可以使用每晚版本的 Rust,请使用 retain
  2. 克隆密钥并在 for 循环中使用
  3. 迭代两次。第一次迭代时,将要删除的键存储到某处,并在第二次迭代时从块中删除这些键

我希望有更好的解决方案

,

据我所知,这个问题有几个解决方案。如果您能够使用夜间功能,则可以使用 BTreeMap 上的保留方法。请参阅:https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.retain。后期编辑:最新的 Rust 版本 (1.53.0) 已经稳定了 BTreeMap 中的 retain 方法,因此现在可以在没有夜间功能的情况下使用它。有关详细信息,请参阅 https://blog.rust-lang.org/2021/06/17/Rust-1.53.0.html

或者,由于您的密钥似乎是 u32,因此可以轻松复制它们。以下内容应该可以解决问题:

    pub fn update_confirmed_height(&mut self,new_confirmed_height: u32) {
        let keys = {
            let mut keys = vec![];
            for (key,_) in self.map.iter() {
                if key < &new_confirmed_height {
                    keys.push(*key);    
                }
            }
            
            keys
        };
        
        for key in keys {
            self.map.remove(&key);
        }
    }

另一种解决方案是改变实际地图本身,并创建一个新地图。这将,至少在这个快速版本不在我的脑海中,需要一个值的克隆。

    pub fn update_confirmed_height(&mut self,new_confirmed_height: u32) {
        self.blocks = {
            let mut new_blocks = BTreeMap::new();
            for (key,value) in self.blocks.iter() {
                if key < &new_confirmed_height {
                    new_blocks.insert(*key,value.clone());    
                }
            }
            
            new_blocks
        }
    }

如果你问我,重要的是理解编译器显示该错误的“原因”,以及上述解决方案为何以及如何解决该问题。一旦你这样做了,你也许可以通过一些时间和思考提出比上述更好的解决方案。