2015-06-15 11 views
10

Ich versuche, einige Spielzeug-Code zu schreiben, die die Anzahl speichert, wie oft es ein Wort in einem HashMap sieht. Wenn der Schlüssel vorhanden ist, erhöht er einen Zähler um eins, wenn der Schlüssel nicht existiert, addiert er diesen mit dem Wert 1. Ich möchte instinktiv diese mit einem Muster Spiel zu tun, aber ich traf eine borrow wandelbar mehr als einmal Fehler:Willst du zu HashMap hinzufügen mit Muster übereinstimmen, erhalten mehr als einmal zu borgen veränderbar

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

Der Fehler, den ich bekommen ist:

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 

Ich verstehe, warum der Compiler ist mürrisch: Ich habe ihm gesagt, dass ich den auf word getasteten Wert mutieren werde, aber dann ist die Einfügung nicht auf diesem Wert. Allerdings ist die Einfügung auf einer None, also hätte ich gedacht, der Compiler hätte erkannt, dass es keine Chance gab, jetzt c[s] zu mutieren.

Ich fühle mich wie diese Methode sollte funktionieren, aber mir fehlt ein Trick. Was mache ich falsch?

EDIT: Ich weiß, ich kann diese

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

aber dies scheint schrecklich hässlich Code vs der Mustererkennung verwendet tun (vor allem mit der contains_key() Prüfung als wenn zu tun, und dann im Wesentlichen, dass der Check wieder zu tun mit . Some

Antwort

10

Sie haben den Eintrag "Muster" verwenden:

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); 
    } 
} 

Der Entry-Objekt Sie einen Wert einfügen können wenn es fehlt, oder um es zu ändern, wenn es bereits existiert.

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

+0

Beachten Sie, dass '' manuell '' '' '' '' '' '' fast nie erforderlich ist. Die Methoden 'or_insert()' und 'or_insert_with()' bieten eine prägnantere Möglichkeit, dies zu erreichen. Siehe die Antwort von A.B. für Informationen über diese. –

8

HashMap::entry() ist die Methode, die hier zu verwenden. In den meisten Fällen wollen Sie mit Entry::or_insert() verwenden, um einen Wert einzufügen:

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

Im Fall der Wert Bedarf eingefügt werden aufwendig berechnet werden, können Sie Entry::or_insert_with() verwenden die Berechnung um sicherzustellen, dass nur dann ausgeführt wird, wenn es braucht, um . Beide or_insert Methoden werden wahrscheinlich alle Ihre Bedürfnisse abdecken. Aber wenn Sie, aus welchem ​​Grund auch immer, etwas anderes machen wollen, können Sie einfach match auf die Entry enum.

+0

Wenn ich "or_insert" nicht verwendet habe, gibt es eine Möglichkeit, dass das von mir angebotene Code-Snippet jemals funktioniert? – cflewis

+0

@cflewis bearbeitet meine Bearbeitung alles? –

Verwandte Themen