Modul 6 — Collections
Vec — das Arbeitspferd
Der Vec ist Rusts Standardliste: wachsende Elemente gleichen Typs, am Stück auf dem Heap:
fn main() {
let mut v = vec![3, 1, 4];
v.push(1);
v.push(5);
println!("{} Elemente", v.len());
println!("zweitens: {}", v[1]);
println!("{v:?}");
}
vec![...] baut die Liste. Intern hält ein Vec ein Triple aus Zeiger,
Länge und Kapazität; wächst er über die Kapazität, wird der Puffer größer
kopiert — push bleibt amortisiert billig.
Sicher zugreifen: Index vs. get
fn main() {
let v = vec![10, 20, 30];
match v.get(10) {
Some(x) => println!("gefunden: {x}"),
None => println!("nicht da"),
}
}
v[10] paniziert bei Überschreitung (Index out of bounds). v.get(10)
gibt Option<&T> zurück — dieselbe Philosophie wie in Modul 5: Abwesenheit
ist ein Wert, kein Absturz.
Iterieren: leihen oder nehmen?
fn main() {
let v = vec![1, 2, 3];
for x in &v {
print!("{x} "); // leiht — v bleibt nutzbar
}
println!();
let sum: i32 = v.iter().sum();
println!("Summe: {sum}, v: {v:?}");
}
&v lehnt die Liste zum Lesen; v (ohne &) würde sie in die Schleife
hineinbewegen. Und zum Filtern in place gibt es retain — statt
während des Iterierens zu löschen (was der Borrow-Checker verboten):
fn main() {
let mut v = vec![3, -1, 4, -5];
v.retain(|&x| x >= 0);
println!("{v:?}");
}
📝 Schnellprüfung
Was ist der Unterschied zwischen v[10] und v.get(10) bei 3 Elementen?
Übung: Vec sicher auswerten
🧩 Übung m6-l1-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
HashMap & das entry-Idiom
Schlüssel auf Wert: HashMap<K, V> ist die Standard-Assoziativliste.
use std::collections::HashMap;
fn main() {
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert(String::from("anna"), 42);
scores.insert(String::from("ben"), 17);
println!("{:?}", scores.get("anna"));
scores.insert(String::from("anna"), 50); // überschreibt
println!("{:?}", scores.get("anna"));
}
insert überschreibt existierende Werte und liefert den alten als Option
zurück. get gibt Option<&V> — fehlen ist ein normaler Fall.
Das entry-Idiom: zählen in einem Lookup
Der Klassiker — Wortzähler ohne Doppel-Lookup:
use std::collections::HashMap;
fn word_counts(text: &str) -> HashMap<&str, u32> {
let mut counts = HashMap::new();
for word in text.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
counts
}
fn main() {
let counts = word_counts("rot grün rot blau grün rot");
for (word, n) in &counts {
println!("{word}: {n}");
}
}
entry(word) holt oder erzeugt den Slot in einem Hash-Lookup,
or_insert(0) impft fehlende Zähler mit 0 — und die zurückgegebene
&mut u32 zählst du direkt hoch. Ohne entry bräuchtest du contains_key
plus get plus insert: drei Lookups, wo einer reicht.
Wann entry statt insert?
insert = „Setze genau diesen Wert“ (Rückfall Wert geht verloren). entry = „Nimm den existierenden Slot und arbeite darauf“ — Zählen, Anhängen, Bedingtes Einfügen. Die HashMap garantiert: pro Schlüssel genau ein Wert.
📝 Schnellprüfung
Was gibt counts.entry(w).or_insert(0) zurück?
Übung: Wortzähler
🧩 Übung m6-l2-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
HashSet, String & die Kollektionswahl
Nicht jede Frage braucht einen Wert — manche braucht nur eine Zugehörigkeit:
„Habe ich das schon gesehen?“ Dafür ist das HashSet da:
use std::collections::HashSet;
fn deduped(values: &[i32]) -> Vec<i32> {
let unique: HashSet<i32> = values.iter().copied().collect();
let mut result: Vec<i32> = unique.into_iter().collect();
result.sort();
result
}
fn main() {
println!("{:?}", deduped(&[4, 2, 4, 1, 2, 9]));
}
Einfügen O(1), Enthaltensein O(1), keine Duplikate — ideal für Mengen, Blacklists, Already-Seen-Markierungen.
String als Schlüssel
Warum sind HashMap-Schlüssel oft String statt &str? Weil der besitzende
String dem Map die Kontrolle über die Lebensdauer gibt — und Suchen
funktionieren trotzdem mit Literalen:
use std::collections::HashMap;
fn main() {
let mut cap: HashMap<String, String> = HashMap::new();
cap.insert(String::from("de"), String::from("Hallo"));
cap.insert(String::from("en"), String::from("Hello"));
// &str-Lookup dank Borrow-Trait:
println!("{:?}", cap.get("de"));
}
Regel: Anforderungen an Schlüssel sind Eq + Hash. Für sortierte Iteration
und Range-Abfragen gibt es das BTreeMap — gleiche API, Baum statt Hash,
dadurch immer in Schlüsselreihenfolge.
Die Kollektionswahl in 10 Sekunden
- Liste, Reihenfolge zählt → der Vec
- Schlüssel zu Wert, schnell →
HashMap<K, V> - „Schon gesehen?“ / Eindeutigkeit → das HashSet
- Sortierte Reihenfolge / Ranges →
BTreeMap<K, V> - Warteschlange an beiden Enden → die VecDeque
📝 Schnellprüfung
Du brauchst jeden Begriff eines Texts genau einmal, Reihenfolge egal. Was nimmst du?
Übung: Duplikate raus
🧩 Übung m6-l3-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Mini-Projekt: Anagramm-Gruppen
Das Abschlussprojekt von Modul 6 kombiniert HashMap, Vec, Sorting und das entry-Idiom zu einem Klassiker der Praktischen Informatik:
🏢 Praxis-Szenario: Die Wortschatz-App
Eine Lern-App soll Wortgruppen mit denselben Buchstaben zusammenzeigen — Anagramme wie Lager und Regal. Der erste Prototyp vergleicht jedes Wort mit jedem: bei 10.000 Wörtern 100 Millionen Vergleiche. Mit der richtigen Datenstruktur wird daraus ein einziger Durchlauf.
🧩 Übung m6-l4-e1
🔮 Vorhersage: Was passiert beim Ausführen?
Was du in Modul 6 geschafft hast
- Vec sicher ausgewertet (get statt Index, retain statt remove-in-Loop)
- HashMap mit entry-Idiom: zählen in einem Lookup
- HashSet für Eindeutigkeit, BTreeMap für Ordnung
- Ein echtes Gruppierungsproblem in O(n · k log k) gelöst
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Modul-Checkpoint
Checkpoint: Modul 6
Bestehensgrenze: 80 %. Der Versuch wird bewertet und zählt für das Modul-Gate.
Bereit, dein Wissen zu testen?
Probiere unsere Übungsprüfungen mit Hunderten von realistischen Fragen aus.
Üben starten →