Im Rust-Programm ist das Gedächtnismanagement traditionell eines der komplexesten Probleme in der Low-Level-Programmierung. Vor der Einführung von Rust erforderten viele Sprachen ein manuelles Gedächtnismanagement (wie C/C++), was zu Speicherlecks und Datenbeschädigungen führte. Rust hat einen anderen Ansatz gewählt — Sammlungen wie Vec<T> verwenden eine automatische und sichere Strategie für das Gedächtnismanagement, indem sie die Momente der Allokation, Reallokation (Resize) und Freigabe durch ein Besitz- und Borrowing-System kontrollieren.
Das Problem bestand darin, dass die meisten Sprachen entweder die Details des Allokators (GC) zu stark abstrahieren oder den Programmierer für alles verantwortlich machen (malloc/free). Im Fall von dynamischen Arrays ist es äußerst wichtig, auf Speicherlecks und Array-Grenzen zu achten, sowie das Besitzrecht nicht zu verletzen.
Die Lösung in Rust ist die Automatisierung durch sichere Abstraktionen. Vec<T> allokiert Speicher auf dem Heap, erhöht die Größe dynamisch (üblicherweise mit exponentiellem Wachstum) und gibt alles beim Verlassen des Geltungsbereichs frei (RAII).
Beispielcode:
fn main() { let mut v: Vec<i32> = Vec::new(); v.push(1); v.push(2); v.push(3); // Das Hinzufügen führt zu einer Größenänderung und Speicherreallokation println!("Vektor: {:?}", v); // Beim Verlassen von main wird der Speicher automatisch freigegeben }
Wichtige Merkmale:
Vec<T> allokiert den Speicher im Voraus und reallokiert ihn bei Bedarf.Welche Wachstums-Komplexität hat das Hinzufügen von Elementen zu Vec?
Normalerweise ist die Komplexität von push amortisiert O(1), jedoch wenn das Array überläuft, wird ein neuer Speicherbereich allokiert (der Größe wird ungefähr verdoppelt) und alle Elemente werden kopiert. Dieser Moment ist die einzige Ausnahme, in der die Operation O(n) wird.
Was passiert, wenn ich versuche, ein Element außerhalb des Bereichs über v[index] zu erhalten?
Die Verwendung von eckigen Klammern führt zu einem Panic beim Überschreiten der Grenzen. Es ist erforderlich, die Methode .get() zu verwenden, die Option zurückgibt und die Fehler sicher behandelt.
let element = v.get(10); // None, wenn der Index nicht existiert
Kann ich auf ein Element in Vec verweisen, nachdem das Vektor möglicherweise gewachsen ist (resize)?
Nein, nach einer Größenänderung des Vektors (zum Beispiel durch push bei Überlauf) kann alles Utiles verschoben werden, und alte Verweise werden ungültig — dies führt zu einem Kompilierungsfehler (oder undefined behavior im unsafe-Block, wenn Sie sie manuell verwenden).
Ein Entwickler implementiert einen Cache für Nachrichten auf Basis von Vec<T> und gibt externe Referenzen auf Elemente zurück. Nach einem neuen Einfügen erfolgt eine Speicherreallokation, und alle vorhandenen Referenzen werden "hängend". Das führt zu einem Absturz der Anwendung.
Vorteile:
Nachteile:
Es wird entweder eine interne Identifizierung der Elemente (Indizes/Schlüssel + Validierungsprüfung) verwendet, oder es werden nur Kopien/unveränderbare Werte zurückgegeben, das Halten von langlebigen Referenzen auf Elemente von Vec ist nicht gestattet.
Vorteile:
Nachteile: