作者:94Wong_386 | 来源:互联网 | 2023-01-22 18:32
这在任何语言中都应该是一项微不足道的任务.这不适用于Rust.
use std::collections::HashMap;
fn do_it(map: &mut HashMap) {
for (key, value) in map {
println!("{} / {}", key, value);
map.remove(key);
}
}
fn main() {}
这是编译器错误:
error[E0382]: use of moved value: `*map`
--> src/main.rs:6:9
|
4 | for (key, value) in map {
| --- value moved here
5 | println!("{} / {}", key, value);
6 | map.remove(key);
| ^^^ value used here after move
|
= note: move occurs because `map` has type `&mut std::collections::HashMap`, which does not implement the `Copy` trait
为什么要试图移动参考?从文档中,我不认为移动/借用适用于引用.
1> Shepmaster..:
至少有两个原因导致不允许这样做:
你需要有两个并发的可变引用map
- 一个由for
循环中使用的迭代器保持,一个在变量map
中调用map.remove
.
你必须键和值参考范围内试图突变地图时的地图.如果允许您以任何方式修改地图,这些引用可能会失效,从而为内存不安全打开了大门.
核心Rust原则是别名XOR Mutability.您可以对值具有多个不可变引用,或者您可以对其进行单个可变引用.
我不认为移动/借用适用于参考文献.
每种类型都受Rust的移动规则以及可变别名的限制.请让我们知道文档的哪些部分说不是,所以我们可以解决这个问题.
为什么要试图移动参考?
这由两部分组成:
您只能有一个可变引用
for
循环取值按值迭代
当你打电话时for (k, v) in map {}
,所有权map
转移到for循环,现在已经消失.
我将执行map(&*map
)的不可变借用并迭代它.最后,我清楚整个事情:
fn do_it(map: &mut HashMap) {
for (key, value) in &*map {
println!("{} / {}", key, value);
}
map.clear();
}
使用以字母"A"开头的键删除每个值
我用的是HashMap::retain
:
fn do_it(map: &mut HashMap) {
map.retain(|key, value| {
println!("{} / {}", key, value);
!key.starts_with("a")
})
}
当地图实际被修改时,这保证了key
并且value
不再存在,因此任何借用它们现在都已经消失了.
2> Peter Hall..:
这在任何语言中都应该是一项微不足道的任务.
当你在迭代它时,Rust会阻止你改变地图.在大多数语言中,这是允许的,但通常行为没有明确定义,删除项目可能会干扰迭代,从而影响其正确性.
为什么要试图移动参考?
HashMap
实现IntoIterator
,所以你的循环相当于:
for (key, value) in map.into_iter() {
println!("{} / {}", key, value);
map.remove(key);
}
如果你看一下定义into_iter
,你会发现它需要self
,而不是&self
或&mut self
.您的变量map
是一个引用,因此它被隐式取消引用以获取self
,这就是错误说*map
已被移动的原因.
有意构建API,以便在循环结构时不会做任何危险.循环完成后,结构的所有权将被放弃,您可以再次使用它.
一种解决方案是跟踪要在a中删除的项目Vec
,然后将其删除:
fn do_it(map: &mut HashMap) {
let mut to_remove = Vec::new();
for (key, value) in &*map {
if key.starts_with("A") {
to_remove.push(key.to_owned());
}
}
for key in to_remove.iter() {
map.remove(key);
}
}
您还可以使用迭代器将地图过滤为新地图.也许是这样的:
fn do_it(map: &mut HashMap) {
*map = map.into_iter().filter_map(|(key, value)| {
if key.starts_with("A") {
None
} else {
Some((key.to_owned(), value.to_owned()))
}
}).collect();
}
但我刚看到Shepmaster的编辑 - 我忘记了retain
,哪个更好.它更简洁,不会像我一样做不必要的复制.