Einführung
Ein Vektor ordnet Werte anhand numerischer Positionen an. Eine HashMap<K, V> verknüpft dagegen jeden eindeutigen Schlüssel des Typs K mit genau einem Wert des Typs V. Dadurch eignet sie sich für Zähler, Einstellungen und andere Daten mit Schlüsseln.
Sie erstellen eine kleine Map zur Worthäufigkeit, rufen einen Zähler ab und ersetzen ihn, verwenden die Entry-API für vorhandene und fehlende Wörter und durchlaufen alle Werte, ohne sich auf die nicht festgelegte Reihenfolge der Schlüssel zu verlassen.
Werte mit Schlüsseln einfügen und abrufen
In diesem Schritt fügen Sie zwei Schlüssel-Wert-Paare ein und rufen einen Zähler sicher ab.
Wechseln Sie in das Projektverzeichnis und öffnen Sie den Quellcode:
cd /home/labex/project/word-counts
nano src/main.rs
Der vorbereitete Pfad std::collections::HashMap bezeichnet HashMap innerhalb des Collection-Moduls der Rust-Standardbibliothek. Die use-Zeile gibt diesem langen Pfad den kurzen lokalen Namen HashMap. Betrachten Sie dies vorerst als vorbereitete Importstruktur. Pfade und Imports werden in den späteren Modulen des Labs ausführlich behandelt. HashMap<String, u32> speichert eigene String-Schlüssel und vorzeichenlose Ganzzahlzähler. Ersetzen Sie den Kommentar zu Schritt 1a durch:
let _ = counts.insert(String::from("rust"), 1);
let _ = counts.insert(String::from("safe"), 1);
insert gibt den Wert zurück, der zuvor unter diesem Schlüssel gespeichert war. Da diese Schlüssel neu sind, ist das Ergebnis None. Das Muster let _ = ...; wertet die Operation aus und verwirft ihr Ergebnis absichtlich. Dieses alleinstehende _ unterscheidet sich von einem Namen wie _returned_id: Der alleinstehende Platzhalter erstellt keine Bindung, die Sie später verwenden können.
Ersetzen Sie den Kommentar zu Schritt 1b durch:
match counts.get("rust") {
Some(count) => println!("Rust count: {count}"),
None => println!("Rust count: missing"),
}
get akzeptiert den geliehenen Text "rust" und gibt ein Option<&u32> zurück, ohne den Schlüssel oder den Zähler aus der Map zu verschieben. Speichern und beenden Sie den Editor. Prüfen und starten Sie anschließend das Programm:
cargo check
cargo run --quiet
Unique words: 2
Rust count: 1
Die erste Zeile zeigt, dass die Schlüssel eindeutig sind. Die zweite Zeile zeigt, dass der gespeicherte Zähler gefunden wurde.
Einen vorhandenen Zähler ersetzen
In diesem Schritt verwenden Sie insert mit einem bereits vorhandenen Schlüssel und prüfen den ersetzten Wert.
Öffnen Sie den Quellcode:
nano src/main.rs
Ersetzen Sie den Kommentar zu Schritt 2 durch:
let previous = counts.insert(String::from("rust"), 2);
println!("Previous rust count: {previous:?}");
Eine Map speichert pro Schlüssel nur einen Wert. Der neue Wert 2 ersetzt 1, und das zurückgegebene Some(1) macht diese Ersetzung sichtbar. Speichern und beenden Sie den Editor. Führen Sie anschließend das Programm aus:
cargo run --quiet
Die neue Zeile sollte lauten:
Previous rust count: Some(1)
Die Anzahl der eindeutigen Wörter bleibt zwei, weil durch das Ersetzen kein weiterer Schlüssel entsteht.
Zähler mit der Entry-API aktualisieren
In diesem Schritt erhöhen Sie über dieselbe API den Zähler eines vorhandenen und eines fehlenden Wortes.
entry(key).or_insert(default) gibt eine veränderbare Referenz auf den vorhandenen Wert des Schlüssels zurück. Ist der Schlüssel nicht vorhanden, fügt die Methode den Standardwert ein und gibt eine veränderbare Referenz auf diesen Wert zurück. Öffnen Sie den Quellcode:
nano src/main.rs
Ersetzen Sie den Kommentar zu Schritt 3 durch:
*counts.entry(String::from("safe")).or_insert(0) += 1;
*counts.entry(String::from("fast")).or_insert(0) += 1;
println!("Safe count: {}", counts["safe"]);
println!("Fast count: {}", counts["fast"]);
Lesen Sie die kompakte Aktualisierung von innen nach außen. Zuerst wählt entry(key) die zum Schlüssel gehörende Stelle aus, unabhängig davon, ob sie belegt oder frei ist. Danach stellt or_insert(0) sicher, dass ein Wert vorhanden ist, und gibt eine veränderbare Referenz, &mut u32, auf diesen Wert zurück.
Das führende * greift auf den u32 hinter dieser Referenz zu. Schließlich addiert += 1 den Wert eins und speichert den neuen Zähler wieder in der Map.
Daher wird safe zu 2, während das bisher fehlende fast mit null beginnt und zu 1 wird. Mit eckigen Klammern lesen Sie anschließend einen Wert anhand seines Schlüssels. Der Zugriff per Index würde eine Panic auslösen, wenn der Schlüssel fehlt. Hier ist er nur sicher, weil die beiden vorherigen entry-Aufrufe garantieren, dass beide Schlüssel vorhanden sind. Verwenden Sie weiterhin get, wenn ein Schlüssel fehlen kann.
Speichern und beenden Sie den Editor. Führen Sie anschließend das Programm aus:
cargo run --quiet
Die letzten Zeilen sollten nun lauten:
Safe count: 2
Fast count: 1
Eine Operation behandelte sowohl den Fall eines vorhandenen als auch den eines fehlenden Schlüssels.
Durchlaufen, ohne von der Schlüsselreihenfolge abzuhängen
In diesem Schritt durchlaufen Sie alle gespeicherten Zähler und berechnen eine von der Reihenfolge unabhängige Gesamtsumme.
HashMaps versprechen keine stabile Reihenfolge beim Durchlaufen der Schlüssel. Bei einer anfängerfreundlichen Überprüfung sollten Sie daher nicht erwarten, dass die Schlüssel in einer bestimmten Reihenfolge ausgegeben werden. Die Methode values() besucht jeden gespeicherten Wert und reicht für die Berechnung einer Gesamtsumme aus.
Öffnen Sie den Quellcode und ersetzen Sie den Kommentar zu Schritt 4:
nano src/main.rs
let mut total = 0;
for count in counts.values() {
total += count;
}
println!("Total occurrences: {total}");
count ist hier eine gemeinsam genutzte Referenz, und += kann die referenzierte Ganzzahl addieren. Speichern und beenden Sie den Editor. Prüfen und starten Sie anschließend das Programm:
cargo check
cargo run --quiet
Die letzte Zeile sollte lauten:
Total occurrences: 5
Die Gesamtsumme setzt sich aus rust = 2, safe = 2 und fast = 1 zusammen, ohne von der Reihenfolge beim Durchlaufen der Map abzuhängen.
Zusammenfassung
Sie haben Zähler mit Schlüsseln eingefügt, abgerufen, ersetzt und erhöht. Anschließend haben Sie alle Werte der Map durchlaufen, ohne eine stabile Schlüsselreihenfolge vorauszusetzen.


