[英]How to iterate through a Hashmap, print the key/value and remove the value in Rust?
This should be a trivial task in any language.这在任何语言中都应该是一项微不足道的任务。 This isn't working in Rust.这在 Rust 中不起作用。
use std::collections::HashMap;
fn do_it(map: &mut HashMap<String, String>) {
for (key, value) in map {
println!("{} / {}", key, value);
map.remove(key);
}
}
fn main() {}
Here's the compiler error:这是编译器错误:
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<std::string::String, std::string::String>`, which does not implement the `Copy` trait
Why it is trying to move a reference?为什么它试图移动引用? From the documentation, I didn't think moving/borrowing applied to references.从文档中,我不认为移动/借用适用于参考。
There are at least two reasons why this is disallowed:不允许这样做的原因至少有两个:
You would need to have two concurrent mutable references to map
— one held by the iterator used in the for
loop and one in the variable map
to call map.remove
.您需要有两个对map
并发可变引用——一个由for
循环中使用的迭代器保存,另一个保存在变量map
以调用map.remove
。
You have references to the key and the value within the map when trying to mutate the map.你有钥匙,并试图变异地图时地图内的值引用。 If you were allowed to modify the map in any way, these references could be invalidated, opening the door for memory unsafety.如果您被允许以任何方式修改映射,这些引用可能会失效,从而为内存不安全打开大门。
A core Rust principle is Aliasing XOR Mutability . Rust 的一个核心原则是Aliasing XOR 可变性。 You can have multiple immutable references to a value or you can have a single mutable reference to it.您可以有多个对一个值的不可变引用,也可以有一个对它的可变引用。
I didn't think moving/borrowing applied to references.我不认为移动/借用适用于参考。
Every type is subject to Rust's rules of moving as well as mutable aliasing.每种类型都遵循 Rust 的移动规则和可变别名。 Please let us know what part of the documentation says it isn't so we can address that.请让我们知道文档的哪一部分不是这样我们可以解决这个问题。
Why it is trying to move a reference?为什么它试图移动引用?
This is combined of two parts:这由两部分组合而成:
Copy
trait你只能有一个可变引用,所以可变引用不实现Copy
traitfor
loops take the value to iterate over by value for
循环取值按值迭代When you call for (k, v) in map {}
, the ownership of map
is transferred to the for loop and is now gone.当您for (k, v) in map {}
调用for (k, v) in map {}
的所有权map
转移到 for 循环,现在已经消失了。
I'd perform an immutable borrow of the map ( &*map
) and iterate over that.我会执行地图( &*map
)的不可变借用并对其进行迭代。 At the end, I'd clear the whole thing:最后,我会清除整个事情:
fn do_it(map: &mut HashMap<String, String>) {
for (key, value) in &*map {
println!("{} / {}", key, value);
}
map.clear();
}
remove every value with a key that starts with the letter "A"使用以字母“A”开头的键删除每个值
I'd use HashMap::retain
:我会使用HashMap::retain
:
fn do_it(map: &mut HashMap<String, String>) {
map.retain(|key, value| {
println!("{} / {}", key, value);
!key.starts_with("a")
})
}
This guarantees that key
and value
no longer exist when the map is actually modified, thus any borrow that they would have had is now gone.这保证了在实际修改映射时key
和value
不再存在,因此它们本应拥有的任何借用现在都消失了。
This should be a trivial task in any language.这在任何语言中都应该是一项微不足道的任务。
Rust is preventing you from mutating the map while you are iterating over it. Rust 会阻止您在迭代地图时对其进行变异。 In most languages this is allowed, but often the behaviour is not well-defined, and removal of the item can interfere with the iteration, compromising its correctness.在大多数语言中,这是允许的,但通常行为没有明确定义,删除项目会干扰迭代,影响其正确性。
Why it is trying to move a reference?为什么它试图移动引用?
HashMap
implements IntoIterator
, so your loop is equivalent to : HashMap
实现IntoIterator
, 所以你的循环相当于:
for (key, value) in map.into_iter() {
println!("{} / {}", key, value);
map.remove(key);
}
If you look at the definition of into_iter
, you'll see that it takes self
, not &self
or &mut self
.如果您查看into_iter
的定义,您会发现它需要self
,而不是&self
或&mut self
。 Your variable map
is a mutable reference, and IntoIterator
is implemented for &mut HashMap
- the self
in into_iter
is the &mut HashMap
, not the HashMap
.您的变量map
是一个可变参考,并IntoIterator
是为实现&mut HashMap
-在self
中into_iter
是&mut HashMap
,而不是HashMap
。 Mutable references cannot be copied (since only one mutable reference to any data can exist at one time) so this mutable reference is moved.无法复制可变引用(因为一次只能存在一个对任何数据的可变引用),因此该可变引用被移动。
The API is intentionally built that way so that you can't do anything dangerous while looping over a structure. API 是有意以这种方式构建的,因此您在遍历结构时不能做任何危险的事情。 Once the loop is complete, the ownership of the structure is relinquished and you can use it again.循环完成后,结构的所有权将被放弃,您可以再次使用它。
One solution is to keep track of the items you intend to remove in a Vec
and then remove them afterwards:一种解决方案是跟踪您打算在Vec
删除的项目,然后再删除它们:
fn do_it(map: &mut HashMap<String, String>) {
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);
}
}
You may also use an iterator to filter the map into a new one.您还可以使用迭代器将地图过滤为新的地图。 Perhaps something like this:也许是这样的:
fn do_it(map: &mut HashMap<String, String>) {
*map = map.into_iter().filter_map(|(key, value)| {
if key.starts_with("A") {
None
} else {
Some((key.to_owned(), value.to_owned()))
}
}).collect();
}
But I just saw Shepmaster's edit - I had forgotten about retain
, which is better.但我刚刚看到 Shepmaster 的编辑 - 我忘记了retain
,这是更好的。 It's more concise and doesn't do unnecessary copying as I have done.它更简洁,不会像我所做的那样进行不必要的复制。
rust actually supports a wide number of potential solutions to this problem, though i myself also found the situation to be a bit confusing at first, and again each time i need a more complicated treatment of my hashmaps. Rust 实际上支持针对这个问题的大量潜在解决方案,尽管我自己也发现这种情况起初有点令人困惑,而且每次我需要对我的哈希图进行更复杂的处理时。
.drain()
.要在删除项目时迭代它们,请使用.drain()
。 .drain()
has the advantage of taking/owning rather than borrowing the values. .drain()
具有获取/拥有而不是借用值的优势。.drain_filter()
.如果您只想有条件地删除其中一些,请使用.drain_filter()
。.drain_filter()
's closure argument, but this will check for removal after the changes.如果您需要改变每个项目但只想删除其中的一些,您可以在.drain_filter()
的闭包参数中改变它们,但这将在更改后检查是否删除。.drain_filter()
them in another for loop or a map.一个稍微慢一点但可能更清楚的替代方法是在一个 for 循环中改变它们,然后在另一个 for 循环或映射中.drain_filter()
它们。.clear()
to remove all elements, after you're done iterating through them to print them.你也可以调用.clear()
来删除所有元素,在你完成遍历它们以打印它们之后。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.