ハッシュマップを反復処理し、キー/値を出力し、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() {}
コンパイラエラーは次のとおりです。
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
なぜ参照を移動しようとしているのですか?ドキュメントから、参照に移動/借入が適用されるとは思わなかった。
これが許可されない少なくとも2つの理由があります。
map
への2つの同時可変参照が必要です。1つはfor
ループで使用されるイテレーターによって保持され、もう1つは_map.remove
_を呼び出す変数map
で保持されます。 。マップを変更しようとすると、キーと値への参照withinがあります。何らかの方法でマップの変更が許可された場合、これらの参照が無効になり、メモリの安全性が失われる可能性があります。
コアRust原則はエイリアシングXOR Mutability。複数の不変値への参照、または単一の可変参照を使用できます。
移動/借入が参照に適用されるとは思わなかった。
すべてのタイプは、Rustの移動規則と可変エイリアスの規則に従います。ドキュメントのどの部分がそうではないと言っているのか教えてください。
なぜ参照を移動しようとしているのですか?
これは2つの部分で構成されています。
- 単一の可変参照のみを持つことができます
for
ループ 値で反復する値を取得
for (k, v) in map {}
を呼び出すと、map
の所有権がforループに転送され、現在は削除されています。
マップ(_&*map
_)の不変の借用を実行し、それを繰り返します。最後に、すべてを明確にします。
_fn do_it(map: &mut HashMap<String, String>) {
for (key, value) in &*map {
println!("{} / {}", key, value);
}
map.clear();
}
_
文字「A」で始まるキーを持つすべての値を削除します
私は _HashMap::retain
_ を使用します:
_fn do_it(map: &mut HashMap<String, String>) {
map.retain(|key, value| {
println!("{} / {}", key, value);
!key.starts_with("a")
})
}
_
これにより、マップが実際に変更されたときにkey
とvalue
が存在しないことが保証されるため、それらが持っていたはずの借用がなくなります。
これはどの言語でも簡単な作業です。
Rustは、マップを変更することを妨げていますwhileほとんどの言語ではこれが許可されていますが、多くの場合、動作は明確に定義されておらず、アイテムを削除すると反復が妨げられ、正確さが損なわれます。
なぜ参照を移動しようとしているのですか?
HashMap
はIntoIterator
を実装します。 つまり、ループは同等です :
for (key, value) in map.into_iter() {
println!("{} / {}", key, value);
map.remove(key);
}
into_iter
の定義 を見ると、&self
や&mut self
ではなく、self
が必要であることがわかります。変数map
は参照であるため、self
を取得するために暗黙的に逆参照されます。そのため、エラーは*map
が移動されたことを示しています。
APIはそのように意図的に構築されているため、構造をループしている間は危険なことはできません。ループが完了すると、構造の所有権は放棄され、再び使用できます。
1つの解決策は、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);
}
}
イテレータを使用して、マップを新しいものにフィルタリングすることもできます。おそらくこのようなもの:
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();
}
しかし、Shepmasterの編集を見たばかりです。retain
のことを忘れていました。それはより簡潔で、私がやったように不必要なコピーをしません。