Rust Datenstrukturen Leitfaden: Vektoren, HashMaps, Sets, und mehr
Grace Collins
Solutions Engineer · Leapcell

Die Rust-Standardbibliothek bietet grundlegende Datenstrukturen wie Vektoren (Vec<T>
), Hash Maps (HashMap<K, V>
) und Hash Sets (HashSet<T>
). Diese drei Datenstrukturen sind die gebräuchlichsten und nützlichsten in den meisten Programmierszenarien. Ihr Design stimmt mit den Zielen von Rust überein, eine sichere, nebenläufige und praktische Programmiersprache anzubieten. Diese Strukturen decken die meisten Datenspeicher- und Zugriffsbedürfnisse ab und erhalten gleichzeitig die schlanke und effiziente Natur der Rust-Standardbibliothek.
Vektoren (Vec<T>
)
Ein Vektor ist Rusts am häufigsten verwendete dynamische Array-Implementierung. Er bietet schnellen indizierten Zugriff, dynamische Größenänderung und effiziente Traversierung aufgrund seiner zusammenhängenden Speichernutzung, die moderne CPU-Caching-Mechanismen nutzt.
fn main() { // Einen leeren Vektor erstellen let mut numbers: Vec<i32> = Vec::new(); // Einen Vektor mit einem Makro erstellen und initialisieren let mut names = vec!["Alice", "Bob", "Carol"]; // Elemente zum Vektor hinzufügen numbers.push(1); numbers.push(2); numbers.push(3); // Elemente aus dem Vektor entfernen numbers.pop(); // Entfernt das letzte Element und gibt es zurück // Auf Elemente im Vektor zugreifen if let Some(first_name) = names.get(0) { println!("Der erste Name ist: {}", first_name); } // Vektor-Elemente durchlaufen for name in &names { println!("{}", name); } // Elemente im Vektor ändern if let Some(first_name) = names.get_mut(0) { *first_name = "Dave"; } // Einen Iterator verwenden, um Vektor-Elemente zu verarbeiten let numbers_squared: Vec<i32> = numbers.iter().map(|&x| x * x).collect(); println!("Quadrierte Zahlen: {:?}", numbers_squared); // Den Vektor mit zusätzlichen Elementen erweitern numbers.extend([4, 5, 6].iter().copied()); // Direkt auf Elemente über einen Index zugreifen println!("Zweiter Name: {}", names[1]); // Hinweis: Direkte Indizierung kann panik auslösen }
Vektoren sind ideal für die Handhabung von Sequenzen von Elementen desselben Typs, egal ob es sich um Strings, Integer oder benutzerdefinierte Typen handelt. Sie ermöglichen einfaches Hinzufügen, Entfernen und wahlfreien Zugriff auf Elemente.
Hash Maps (HashMap<K, V>
)
Eine Hash Map bietet Key-Value-Speicherung, die mit einer Hash-Tabelle implementiert wird. Sie unterstützt schnelles Nachschlagen, Einfügen und Löschen, was sie zu einer kritischen Datenstruktur für effizienten Datenabruf und -verwaltung macht.
use std::collections::HashMap; fn main() { // Eine leere Hash Map erstellen let mut book_reviews: HashMap<String, String> = HashMap::new(); // Elemente zur Hash Map hinzufügen book_reviews.insert("The Hobbit".to_string(), "Exzellentes Fantasy-Buch".to_string()); book_reviews.insert("The Catcher in the Rye".to_string(), "Klassischer Roman".to_string()); // Auf Elemente in der Hash Map zugreifen if let Some(review) = book_reviews.get("The Hobbit") { println!("Rezension zu Der Hobbit: {}", review); } // Elemente aus der Hash Map entfernen book_reviews.remove("The Catcher in the Rye"); // Über die Hash Map iterieren for (book, review) in &book_reviews { println!("{}: {}", book, review); } // Elemente in der Hash Map aktualisieren book_reviews.entry("The Hobbit".to_string()).or_insert("Keine Rezension gefunden".to_string()); book_reviews.entry("1984".to_string()).or_insert("Dystopische Science-Fiction".to_string()); let mut scores = HashMap::new(); // Direktes Einfügen mit `insert` scores.insert("Blue", 10); scores.insert("Blue", 25); // Überschreibt den vorherigen Wert // Verwendung von `entry` zum Aktualisieren oder Einfügen scores.entry("Yellow").or_insert(50); // Fügt ein, weil "Yellow" nicht existiert scores.entry("Blue").or_insert(50); // Tut nichts, weil "Blue" bereits existiert // Ergebnis: {"Blue": 25, "Yellow": 50} // Prüfen, ob ein Schlüssel existiert if book_reviews.contains_key("1984") { println!("Rezension für 1984 ist verfügbar."); } }
Hash Maps sind nützlich, wenn Sie schnell über Schlüssel auf Daten zugreifen müssen, z. B. bei der Datenbankindizierung und Cache-Implementierungen. Sie bieten flexible Key-Value-Zuordnungen, wodurch Datenorganisation und -abruf einfach und effizient werden.
Hash Sets (HashSet<T>
)
Ein Hash Set ist eine ungeordnete Sammlung, die eindeutige Elemente speichert. Es wird mit einer Hash-Tabelle implementiert und bietet schnelle Such-, Einfüge- und Löschvorgänge.
use std::collections::HashSet; fn main() { // Eine leere Menge erstellen let mut numbers = HashSet::new(); // Elemente zur Menge hinzufügen numbers.insert(1); numbers.insert(2); numbers.insert(3); // Ein Element aus der Menge entfernen numbers.remove(&3); // Prüfen, ob ein Element in der Menge existiert if numbers.contains(&1) { println!("1 ist in der Menge"); } // Über die Menge iterieren for number in &numbers { println!("{}", number); } // Mengenoperationen: Vereinigung, Schnittmenge, Differenz, Symmetrische Differenz // An diesem Punkt enthält numbers {1, 2} let other_numbers = [2, 3, 4].iter().cloned().collect::<HashSet<_>>(); // other_numbers enthält {2, 3, 4} let union = numbers.union(&other_numbers).cloned().collect::<HashSet<_>>(); println!("Vereinigung: {:?}", union); // Vereinigung: `{1, 2, 3, 4}` (alle eindeutigen Elemente aus beiden Mengen) let intersection = numbers.intersection(&other_numbers).cloned().collect::<HashSet<_>>(); println!("Schnittmenge: {:?}", intersection); // Schnittmenge: `{2}` (gemeinsame Elemente) let difference = numbers.difference(&other_numbers).cloned().collect::<HashSet<_>>(); println!("Differenz: {:?}", difference); // Differenz: `{1}` (Elemente in `numbers`, aber nicht in `other_numbers`) let symmetric_difference = numbers.symmetric_difference(&other_numbers).cloned().collect::<HashSet<_>>(); println!("Symmetrische Differenz: {:?}", symmetric_difference); // Symmetrische Differenz: `{1, 3, 4}` (Elemente, die in jeder Menge einzigartig sind) }
Hash Sets sind besonders nützlich für die Handhabung von Sammlungen eindeutiger Elemente, wie z. B. Benutzer-ID-Listen und Datensätze unter bestimmten Bedingungen. Mengenoperationen wie Vereinigung, Schnittmenge und Differenz bieten leistungsstarke Werkzeuge für die effiziente Handhabung von Sammlungsdaten.
Doppelt verkettete Liste (LinkedList<T>
)
LinkedList<T>
ist eine doppelt verkettete Liste, die von der Rust-Standardbibliothek bereitgestellt wird. Im Vergleich zu Vektoren (Vec<T>
) ermöglichen verkettete Listen ein effizientes Einfügen und Löschen von Elementen, insbesondere am Anfang oder Ende der Liste. Sie schneiden jedoch beim wahlfreien Zugriff schlecht ab.
use std::collections::LinkedList; fn main() { // Eine neue leere verkettete Liste erstellen let mut list: LinkedList<i32> = LinkedList::new(); // Elemente am Ende der Liste hinzufügen list.push_back(1); list.push_back(2); // Elemente am Anfang der Liste hinzufügen list.push_front(0); // Elemente vom Anfang und Ende der Liste entfernen assert_eq!(list.pop_front(), Some(0)); assert_eq!(list.pop_back(), Some(2)); // Über die Liste iterieren for elem in list.iter() { println!("{}", elem); } // Elemente in der Liste ändern (erfordert die Verwendung eines Iterators) let mut iter_mut = list.iter_mut(); if let Some(first_elem) = iter_mut.next() { *first_elem = 3; } // Die geänderte Liste ausgeben println!("Geänderte Liste: {:?}", list); }
Wenn häufiges Hinzufügen oder Löschen am Anfang oder Ende einer Liste erforderlich ist, ist LinkedList
eine gute Wahl, da diese Operationen eine Zeitkomplexität von O(1) haben.
Wenn Ihre Anwendung selten wahlfreien Zugriff benötigt und sich mehr auf die sequenzielle Traversierung konzentriert, ist eine verkettete Liste möglicherweise eine bessere Option als ein Vektor.
B-Baum-Map (BTreeMap<K, V>
)
BTreeMap<K, V>
ist eine Key-Value-Sammlung, die mit einem B-Baum implementiert wird. Sie verwaltet Schlüssel in sortierter Reihenfolge. Im Vergleich zu Hash Maps (HashMap<K, V>
) zeichnet sich BTreeMap
aus, wenn geordnete Schlüssel, Bereichssuchen oder geordnete Traversierungen erforderlich sind.
use std::collections::BTreeMap; fn main() { // Eine neue leere BTreeMap erstellen let mut map: BTreeMap<String, i32> = BTreeMap::new(); // Key-Value-Paare in die BTreeMap einfügen map.insert("apple".to_string(), 3); map.insert("banana".to_string(), 2); map.insert("pear".to_string(), 4); // Den Wert abrufen, der einem Schlüssel entspricht if let Some(v) = map.get("apple") { println!("apple: {}", v); } // Ein Key-Value-Paar entfernen map.remove("banana"); // Über die BTreeMap iterieren for (key, value) in &map { println!("{}: {}", key, value); } // Bereichsabfrage: alle Key-Value-Paare mit Schlüsseln zwischen "apple" und "pear" (einschließlich) abrufen let range = map.range("apple".to_string()..="pear".to_string()); for (key, value) in range { println!("Bereichsabfrage: {}: {}", key, value); } }
BTreeMap
ist eine gute Option, wenn eine automatisch sortierte Map benötigt wird, die besonders für Bereichsabfragen und geordnete Traversierung nützlich ist.
Wenn Ihr Programm häufig Such-, Einfüge- und Löschvorgänge durchführt, bei denen Schlüssel natürlich geordnet sind, kann BTreeMap
besser geeignet sein als HashMap
, da sie die Reihenfolge der Schlüssel beibehält und so Bereichssuchen und geordnete Traversierung erleichtert.
B-Baum-Set (BTreeSet<T>
)
BTreeSet<T>
ist ein Set, das auf einem B-Baum basiert. Es speichert eindeutige Elemente und verwaltet sie in sortierter Reihenfolge. Im Vergleich zu HashSet<T>
unterstützt BTreeSet
geordnete Operationen und Bereichsabfragen, kann aber bei einigen Operationen langsamer sein.
use std::collections::BTreeSet; fn main() { // Ein neues leeres BTreeSet erstellen let mut set: BTreeSet<i32> = BTreeSet::new(); // Elemente zum Set hinzufügen set.insert(12); set.insert(5); set.insert(18); // Prüfen, ob ein Element existiert if set.contains(&12) { println!("Set enthält 12"); } // Ein Element entfernen set.remove(&5); // Über das Set iterieren (Elemente sind in aufsteigender Reihenfolge) for num in &set { println!("{}", num); } // Bereichsabfrage: alle Elemente abrufen, die größer oder gleich 10 und kleiner als 20 sind for num in set.range(10..20) { println!("Bereichsabfrage: {}", num); } }
BTreeSet
ist eine gute Wahl, wenn Sie ein geordnetes Set für schnelle Suchvorgänge, Bereichsabfragen oder geordnete Traversierung benötigen.
Es eignet sich für Szenarien, in denen eindeutige Elemente gespeichert werden müssen und die Elemente eine vergleichbare Beziehung haben.
Binärer Heap (BinaryHeap<T>
)
BinaryHeap<T>
ist eine Implementierung einer Priority Queue, die auf einem binären Heap basiert. Sie ermöglicht das schnelle Einfügen von Elementen und das Entfernen des maximalen (oder minimalen) Elements, je nachdem, ob es sich um einen Max-Heap oder Min-Heap handelt. Standardmäßig ist Rusts BinaryHeap
ein Max-Heap.
use std::collections::BinaryHeap; fn main() { // Einen neuen leeren BinaryHeap erstellen let mut heap = BinaryHeap::new(); // Elemente zum Heap hinzufügen heap.push(1); heap.push(5); heap.push(2); // Das maximale Element im Heap einsehen, ohne es zu entfernen if let Some(max) = heap.peek() { println!("Maximales Element: {}", max); } // Das maximale Element entfernen und zurückgeben println!("Entferntes maximales Element: {}", heap.pop().unwrap()); // Über den Heap iterieren (die Reihenfolge der Iteration ist nicht sortiert) for num in &heap { println!("{}", num); } }
Wenn eine Datenstruktur für den schnellen Zugriff auf und das Entfernen des maximalen (oder minimalen) Elements benötigt wird, ist BinaryHeap
ideal. Dies ist besonders nützlich in Algorithmen wie Dijkstras Algorithmus für den kürzesten Pfad.
BinaryHeap
eignet sich für die Aufgabenplanung, Greedy-Algorithmen oder jedes Szenario, das eine Priority Queue erfordert.
Wir sind Leapcell, Ihre erste Wahl für das Hosten von Rust-Projekten.
Leapcell ist die Serverless-Plattform der nächsten Generation für Webhosting, asynchrone Aufgaben und Redis:
Multi-Sprachen-Unterstützung
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung - keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
Mühelose Skalierbarkeit und Hochleistung
- Auto-Skalierung zur einfachen Bewältigung hoher Parallelität.
- Null Betriebsaufwand - konzentrieren Sie sich einfach auf das Bauen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ