rust - 在 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();
}
解决方案
当你打电话时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);
}
在这里,我将其更改为remove
,insert
以便我们知道我们不会冒险删除我们的密钥。然而,由于与您最初报告的原因完全相同,代码无法编译。
推荐阅读
- cross-validation - R xgboost xgb.cv pred 值:最佳迭代还是最终迭代?
- netsuite - NetSuite - 如何添加新的基础货币?
- unit-testing - 如何强制 Azure+Nunit 上的测试并行化?
- swift - 如何在主 App 结构中初始化 EnviromentObject?
- sql-server - 为 UNION 重命名 JSON 列
- java - 使用最低数量的硬币存储总硬币价值?
- c# - 如何使用 AJAX 填充 DIV 并使其可见
- ios - 一个 UIImagePickerController 用于单个 VC 中的多个 UIImageViews
- sql - Yii2:如何在 find() 中使用 sum 函数?
- ios - 带有未定义符号的快速 cocos xcode 架构错误