19

ある単語をHashMap. キーが存在する場合はカウンターを 1 増やし、キーが存在しない場合は値を追加します1。私は本能的にパターンマッチでこれをやりたいと思っていますが、ボローミュータブルを複数回エラーにしました:

fn read_file(name: &str) -> io::Result<HashMap<String, i32>> {
    let b = BufReader::new(File::open(name)?);
    let mut c = HashMap::new();

    for line in b.lines() {
        let line = line?;
        for word in line.split(" ") {
            match c.get_mut(word) {
                Some(i) => {
                    *i += 1;
                },
                None => {
                    c.insert(word.to_string(), 1);
                }
            }
        }
    }

    Ok(c)
}

私が得るエラーは次のとおりです。

error[E0499]: cannot borrow `c` as mutable more than once at a time
  --> <anon>:21:21
   |
16 |             match c.get_mut(word) {
   |                   - first mutable borrow occurs here
...
21 |                     c.insert(word.to_string(), 1);
   |                     ^ second mutable borrow occurs here
22 |                 }
23 |             }
   |             - first borrow ends here

コンパイラが不機嫌な理由を理解しています: をキーにした値を変更するつもりだと言いましたwordが、挿入はその値にありません。ただし、挿入は にあるNoneため、コンパイラは現在変更の可能性がないことに気付いたのではないかと考えていましたc[s]

この方法は機能するはずですが、トリックがありません。私は何を間違っていますか?

編集:私はこれを使用してこれを行うことができることに気付きました

        if c.contains_key(word) {
            if let Some(i) = c.get_mut(s) {
                *i += 1;
            }
        } else {
            c.insert(word.to_string(), 1);
        }

しかし、これはパターンマッチに対して恐ろしく醜いコードのようです (特にcontains_key()、if としてチェックを実行する必要があり、次にSome.

4

3 に答える 3

15

エントリ「パターン」を使用する必要があります。

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

fn main() {
    let mut words = vec!["word1".to_string(), "word2".to_string(), "word1".to_string(), "word3".to_string()];
    let mut wordCount = HashMap::<String, u32>::new();

    for w in words {
        let val = match wordCount.entry(w) {
           Vacant(entry) => entry.insert(0),
           Occupied(entry) => entry.into_mut(),
        };

        // do stuff with the value
        *val += 1;
    }

    for k in wordCount.iter() {
        println!("{:?}", k);
    }
}

Entry オブジェクトを使用すると、値がない場合は値を挿入したり、値が既に存在する場合は値を変更したりできます。

https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html

于 2015-06-15T18:06:18.820 に答える
13

HashMap::entry()ここで使用する方法です。ほとんどの場合、 with を使用しEntry::or_insert()て値を挿入します。

for word in line.split(" ") {
    *c.entry(word).or_insert(0) += 1;
}

挿入する値を高価に計算する必要がある場合は、 を使用Entry::or_insert_with()して、必要なときにのみ計算が実行されるようにすることができます。どちらのor_insert方法でも、おそらくすべてのニーズをカバーできます。しかし、何らかの理由で何か他のことをしたい場合は、単に列挙型matchで行うことができます。Entry

于 2015-06-15T18:12:26.247 に答える
3

これは基本的にもう問題ではありません。非レキシカル ライフタイム(NLL) を使用すると、コードは問題なくコンパイルされます。 Playground での例

NLL は、コンパイラが借用について推論する新しい方法です。NLL は Rust 2018 (≥ 1.31) で有効になりました。最終的には Rust 2015 でも有効になる予定です。NLL とエディションの詳細については、こちらの公式ブログ投稿をご覧ください。

この特定のケースでは、非常に簡潔であるという理由だけで、AB の回答( ) が最善の解決策だと思います。entry(word).or_insert(0)

于 2019-01-29T22:19:31.693 に答える