Historie der Frage:
Rust wurde ursprünglich als Sprache konzipiert, die den Schwerpunkt auf Speichersicherheit legt. Bei einigen Aufgaben – zum Beispiel beim Arbeiten mit FFI oder Low-Level-Allokatoren – muss jedoch mit raw pointers gearbeitet und der dynamische Speicher manuell verwaltet werden. Solche Aufgaben treten sowohl in der Systemprogrammierung als auch bei Leistungsoptimierungen auf. Daher ist es wichtig zu wissen, wie Rust Speicherlecks, dangling pointers und use-after-free verhindern kann.
Problem:
Raw pointers (*const T, *mut T) sind nicht in das System der Besitz- und Referenzkontrolle in Rust integriert: Sie können auf ungültigen Speicher zeigen, falsch freigegeben oder gar nicht freigegeben werden. Fehler im Umgang mit ihnen können zu UB (undefined behavior), Abstürzen, Sicherheitsanfälligkeiten oder Speicherlecks führen.
Lösung:
Statt raw pointers sollten sichere Typen verwendet werden – Box, Rc, Arc, und für temporäre Referenzen – Borrow-Referenzen. Wenn raw pointers unvermeidlich sind (zum Beispiel für die Arbeit mit C API), wird die gesamte Arbeit in unsafe-Blöcken gekapselt, der Drop sorgfältig organisiert, und wenn möglich werden Crates wie NonNull verwendet. Eine weitere Technik sind RAII-Wrappers und die Minimierung des Lebenszyklus eines Zeigers.
Beispielcode:
fn allocate_in_heap() -> Box<i32> { Box::new(100) } // Der Speicher wird automatisch freigegeben // mit raw pointer unsafe fn leak_memory() { let ptr = libc::malloc(4) as *mut i32; if !ptr.is_null() { *ptr = 42; // libc::free(ptr); // Wenn vergessen wird, freizugeben – Speicherleck! } }
Schlüsselmerkmale:
Garantiert Box die automatische Bereinigung aller enthaltenen Werte bei der Löschung von Box?
Ja, beim Löschen von Box<T> ruft der Destruktor zunächst die Bereinigung der Wrapper selbst und dann rekursiv – aller inneren Daten auf (bis hin zu den Elementen von Vec oder anderen Boxen innerhalb der Struktur T).
Kann man einen raw pointer auf eine Struktur sicher durch mehrere Funktionen übergeben, ohne das Risiko eines use-after-free einzugehen?
Nein, der raw pointer trägt keine Lebenszeitinformationen des Objekts. Der Compiler kann die Sicherheit nicht überprüfen, daher liegt die Verantwortung vollständig beim Entwickler: Wenn das Objekt freigegeben wird, zeigt der raw pointer ins Leere.
Kann Rust Drop zweimal für dieselbe Adresse aufrufen, wenn manuell free oder drop_in_place verwendet wird?
Ja, wenn nach der manuellen Freigabe ein anderer Box/Pointer hinterlassen wird, der auf dasselbe Block verweist, wird bei der Zerstörung des zweiten Exemplar der Drop erneut aufgerufen, was UB verursacht. Man sollte niemals manuell etwas freigeben, was von Box, Vec usw. verwaltet wird.
Ein Programmierer nahm einen raw pointer aus einer externen C-Bibliothek, gab ihn nach der Verwendung nicht frei oder der perfnode-dealloc machte einen Fehler mit der Lebensdauer.
Vorteile:
Nachteile:
Es wird ein RAII-Wrap mit Drop verwendet, der Zeiger wird durch Box oder NonNull gekapselt, alles wird sicher am Ende des Scopes zerstört.
Vorteile:
Nachteile: