2017-08-16 17 views
6

これはどの言語でも簡単な作業です。これはRustでは機能しません。参照を移動しようとしているのはなぜハッシュマップを反復処理して、キー/値を表示し、Rustの値を削除する方法は?

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 

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() {} 

ここでコンパイラエラーですか?ドキュメンテーションから、私は移動/借用が参照に適用されたとは思わなかった。

答えて

9

これは許可されませんなぜ、少なくとも2つの理由があります。

  1. あなたはmapに2つの同時可変の参照を持っている必要があります - 一つの変数mapforループと1で使用されるイテレータで開催されたが、 map.removeに電話してください。

  2. あなたがキーとマップを変異しようとしてマップ内の値への参照を持っています。あなたがマップを何らかの方法で修正することが許可されていれば、これらの参照は無効になり、メモリ不足の扉が開かれる可能性があります。

コアさび原理はエイリアシングXOR可変性あります。値への複数の不変参照を持つこともできますし、単一の可変参照を持つこともできます。

I didn't think moving/borrowing applied to references.

すべての種類は変更可能エイリアシングだけでなく、移動の錆の規則に従うものとします。ドキュメントに記載されていない部分がある場合は、私たちに連絡してください。

Why it is trying to move a reference?

これは、二つの部分を組み合わせている:

  1. あなただけがfor (k, v) in map {}を呼び出すと、mapの所有権がある
  2. forループtake the value to iterate over by value

単一可変の参照を持つことができますforループに転送され、現在はなくなっています。


私はマップ(&*map)の不変ボローを実行し、その上で繰り返すと思います。

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"

私が使用したいHashMap::retain::終わりに、私は全部をクリアしたい

これは、マップが実際に変更されたときにkeyvalueは、もはや存在していることを保証しません
fn do_it(map: &mut HashMap<String, String>) { 
    map.retain(|key, value| { 
     println!("{}/{}", key, value); 

     !key.starts_with("a") 
    }) 
} 

、彼らが持っていたであろうどんな借用も今はなくなった。

+0

私はmap()の 'for(key、value);で同じエラーを得ることができます。 for(key、value)for map {} '、と私はこの答えがそれを説明するとは思わない。 –

+1

これについて考えてみると、 'map.clear()'をループ内で呼び出すとどうなりますか? 'key'と' value'は参照であり、もう何も参照しません。 'clear'と' remove'はどちらも '&mut self'を使用し、borrow-checkerの観点からは同じです。 – loganfsmyth

+0

これは私にはさらに厄介な問題をもたらしましたが、メソッド呼び出しの構文が問題を不明瞭にしていると思われます。 https://play.rust-lang.org/?gist=ecf6d9bdbe8e1ad99e5fb3c35c402d1c&version=stable –

5

This should be a trivial task in any language.

あなたはそれを反復しているながら錆がマップを変異からあなたを妨げています。ほとんどの言語でこれは許可されていますが、動作が明確に定義されていないことが多く、アイテムを削除すると反復が妨げられ、その正確性が損なわれます。

Why it is trying to move a reference?

HashMapIntoIteratorso your loop is equivalent toを実装します。

for (key, value) in map.into_iter() { 
    println!("{}/{}", key, value); 
    map.remove(key); 
} 

あなたはdefinition of into_iterを見れば、あなたはそれがself、ない&selfまたは&mut selfを取ることがわかります。変数mapは参照のため、暗黙的に参照解除されてselfに取得されます。そのため、*mapが移動されたというエラーが表示されます。

APIは意図的に構築されているため、構造をループする際に危険なことはできません。ループが完了すると、構造の所有権が放棄され、再度使用することができます。

一つの解決策は、あなたが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、優れていると忘れていました。それはもっと簡潔で、私がしたように不要なコピーをしません。

関連する問題