首页 > 解决方案 > 在 rust 中从 HashMap 中删除一个随机条目

问题描述

我想从 a 中删除一个随机元素HashMap。但是,我一直收到此错误。

error[E0502]: cannot borrow `self.map` as mutable because it is also borrowed as immutable
  --> src/main.rs:23:9
   |
21 |         let key_to_delete = self.map.keys().skip(x).next().unwrap();
   |                             -------- immutable borrow occurs here
22 |         println!("key_to_delete: {:?}", key_to_delete);
23 |         self.map.remove(&key_to_delete);
   |         ^^^^^^^^^------^^^^^^^^^^^^^^^^
   |         |        |
   |         |        immutable borrow later used by call
   |         mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.

但是,如果我.clone()在该行的末尾添加 a ,错误就消失了。虽然我解决了这个问题(主要是通过试验和错误),但我仍然不明白它为什么会起作用。具体来说,为什么首先出现此错误?不可变引用不应该self.map在行后被删除

let key_to_delete = self.map.keys().skip(x).next().unwrap()

完成执行?在这个例子中,key 的类型是i32,只占用 4 个字节。因此,克隆是可以的。但是,如果键是某个大型结构或clone由于其他原因不可取怎么办?那我应该如何解决这个问题呢?

另一个观察结果是我的 IDE(带有 rust 插件的 Intellij)显示类型key_to_delete&i32如果我没有.clone()i32如果我有。我不确定这是否重要。

任何澄清表示赞赏。

这是我的代码。

use std::collections::HashMap;
use rand::{Rng, thread_rng};

#[derive(Debug)]
struct MyStruct {
    map: HashMap<i32, i32>,
}

impl MyStruct {
    fn new() -> Self {
        MyStruct { map: HashMap::new() }
    }

    fn add(&mut self, key: i32, value: i32) {
        self.map.insert(key, value);
        println!("after add: {:?}", self.map);
    }

    fn delete(&mut self) {
        let x: usize = thread_rng().gen_range(0..self.map.len());
        let key_to_delete = self.map.keys().skip(x).next().unwrap().clone(); // this "clone" is critical
        println!("key_to_delete: {:?}", key_to_delete);
        self.map.remove(&key_to_delete);
        println!("map after delete: {:?}", self.map);
    }
}

fn main() {
    let mut c = MyStruct::new();
    c.add(1, 2);
    c.add(3, 4);
    c.add(5, 6);
    c.delete();
}

标签: rust

解决方案


当你打电话时self.map.keys().skip(x).next().unwrap(),你没有得到钥匙——你得到了对钥匙的引用

Rust 保证这个引用在它存在的时候一直有效——这就是 Rust 借用检查器和内存安全背后的全部想法。在我们的例子中,这意味着 Rust 保证映射中的键(连同关联的值)将存在于映射中,只要您对键的引用存在。

但是 Rust 编译器强制执行它的方式是相当残酷的——它只是不允许您以任何方式更改映射的状态,只要您对键的引用存在。不幸的是,因此,您不能使用您的参考从地图中删除其相应的条目。

为了说明这一点,让我们看一下您的代码稍作修改的版本:

fn delete(&mut self) { //not really a delete at this point
    let x: usize = thread_rng().gen_range(0..self.map.len());
    let key_to_delete = self.map.keys().skip(x).next().unwrap(); // this "clone" is gone
    self.map.insert(123, 456); // <- this will cause a compiler error
    println!("key_to_delete: {:?}", key_to_delete);
    // self.map.insert(123, 456);  // <- this will not cause a compiler error as the reference to the key no longer exists
    println!("map after delete: {:?}", self.map);
}

在这里,我将其更改为removeinsert以便我们知道我们不会冒险删除我们的密钥。然而,由于与您最初报告的原因完全相同,代码无法编译。


推荐阅读