Wie kann ich effizient nach einer HashMap suchen und diese einfügen?

102

Ich möchte Folgendes tun:

  • Suchen Sie Vecnach einem bestimmten Schlüssel und speichern Sie ihn zur späteren Verwendung.
  • Wenn es nicht vorhanden ist, erstellen Sie ein Leerzeichen Vecfür den Schlüssel, behalten Sie es jedoch in der Variablen bei.

Wie geht das effizient? Natürlich dachte ich, ich könnte verwenden match:

use std::collections::HashMap;

// This code doesn't compile.
let mut map = HashMap::new();
let key = "foo";
let values: &Vec<isize> = match map.get(key) {
    Some(v) => v,
    None => {
        let default: Vec<isize> = Vec::new();
        map.insert(key, default);
        &default
    }
};

Als ich es versuchte, gab es mir Fehler wie:

error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:13
   |
7  |     let values: &Vec<isize> = match map.get(key) {
   |                                     --- immutable borrow occurs here
...
11 |             map.insert(key, default);
   |             ^^^ mutable borrow occurs here
...
15 | }
   | - immutable borrow ends here

Am Ende habe ich so etwas gemacht, aber ich mag die Tatsache nicht, dass es die Suche zweimal durchführt ( map.contains_keyund map.get):

// This code does compile.
let mut map = HashMap::new();
let key = "foo";
if !map.contains_key(key) {
    let default: Vec<isize> = Vec::new();
    map.insert(key, default);
}
let values: &Vec<isize> = match map.get(key) {
    Some(v) => v,
    None => {
        panic!("impossiburu!");
    }
};

Gibt es einen sicheren Weg, dies mit nur einem zu tun match?

Yusuke Shinyama
quelle

Antworten:

119

Die entryAPI ist dafür ausgelegt. In manueller Form könnte es so aussehen

use std::collections::hash_map::Entry;

let values: &Vec<isize> = match map.entry(key) {
    Entry::Occupied(o) => o.into_mut(),
    Entry::Vacant(v) => v.insert(default)
};

Oder man kann die kürzere Form verwenden:

map.entry(key).or_insert_with(|| default)

Wenn defaultes in Ordnung / billig zu berechnen ist, auch wenn es nicht eingefügt ist, kann es auch einfach sein:

map.entry(key).or_insert(default)
huon
quelle
Danke für eine schnelle Antwort! Jetzt habe ich gelernt, dass ich mich ein wenig mit den Dokumenten befassen sollte.
Yusuke Shinyama
22
Das Problem mit entry () ist, dass Sie immer den Schlüssel klonen müssen. Gibt es eine Möglichkeit, dies zu vermeiden?
Pascalius
@Pascalius Sie könnten Ihren Schlüsseltyp machen &T(wenn die Schlüssel die Karte überleben, z. B. statische Zeichenfolgen) oder Rc<T>anstelle von T- aber es ist in beiden Fällen nicht schön
kbolino
@Pascalius: Sie können v.key()im Ausdruck für verwenden default, und dann wird ein Verweis auf den Schlüssel erhalten, wie er in der Hashmap vorhanden ist, so dass Sie einen Klon auf diese Weise vermeiden können
Chris Beck