Warum der „Garbage Collector“ in Rust nicht das ist, was Sie denken
\n\nWenn Sie aus der Welt von Python, Java oder C# kommen, ruft das Wort „Garbage Collector“ ein klares Bild hervor: Eine Hintergrundumgebung findet und löscht regelmäßig Objekte, auf die niemand mehr verweist. In Rust ist das anders. Es gibt keinen klassischen GC, aber es gibt das System der Besitzverhältnisse und Ausleihe, das dieselbe Aufgabe – die Verwaltung der Lebensdauer von Objekten – bereits zur Kompilierzeit löst.
\n\nDoch das Leben (und der Code) ist komplexer als ideale Schemata. Manchmal müssen Sie den Besitz eines Objekts zwischen mehreren Teilen eines Programms teilen, zyklische Referenzen erstellen oder mit veränderbaren Daten über unveränderbare Zeiger arbeiten. Genau hier kommen die garbage-collected Objekte in Rust ins Spiel: Rc, Arc, Weak und RefCell. Lassen Sie uns verstehen, wie man ihre Lebensdauer verwaltet, ohne Speicherlecks zu verursachen oder den Verstand zu verlieren.
Dieser Artikel ist ein detaillierter Leitfaden für alle, die verstehen möchten, wie die Verwaltung der Lebensdauer von Objekten in Rust in der Praxis funktioniert. Wir werden die Typen nicht nur auflisten, sondern reale Szenarien zeigen, einschließlich einer beliebten Frage von Stack Overflow (ja, genau der mit tausenden von Aufrufen).
\n\nSchlüsselkonzepte: Besitz, Ausleihe und Referenzzähler
\n\nBevor wir in die Tiefe gehen, erinnern wir uns an die Grundlagen:
\n\n- \n
- Besitz (ownership) – Jeder Wert hat genau einen Besitzer. Wenn der Besitzer den Gültigkeitsbereich verlässt, wird der Wert gelöscht. \n
- Ausleihe (borrowing) – Sie können vorübergehend eine Referenz auf einen Wert „ausleihen“, ohne Besitzer zu werden. Einschränkungen: entweder eine veränderbare Referenz oder beliebig viele unveränderbare. \n
- Referenzzähler (reference counting) – Dies ist ein Mechanismus, der es ermöglicht, mehrere Besitzer eines Werts zu haben. In Rust wird dies durch
Rc(für Single-Thread-Programme) undArc(für Multi-Thread-Programme) implementiert. \n
Wenn wir im Kontext von Rust von „garbage-collected Objekten“ sprechen, meinen wir genau die Objekte, die durch einen Referenzzähler verwaltet werden. Sie werden nicht sofort nach Verlassen des Gültigkeitsbereichs gelöscht, sondern warten, bis der Zähler auf Null fällt. Das ist Lebensdauerverwaltung – Sie kontrollieren, wann der Speicher freigegeben wird.
\n\nRc und Arc: Grundlage der Lebensdauerverwaltung
\n\nRc (Reference Counted) ist ein intelligenter Zeiger, der es ermöglicht, den Besitz eines Werts zwischen mehreren Code-Teilen in einem einzigen Thread zu teilen. Jedes Mal, wenn Sie ein Rc klonen, erhöht sich der Zähler. Wenn der Klon den Gültigkeitsbereich verlässt, verringert er sich. Wenn der Zähler Null erreicht, wird der Wert gelöscht.
Beispiel für die einfache Verwendung von Rc:
use std::rc::Rc;\n\nfn main() {\n let data = Rc::new(vec![1, 2, 3]);\n let a = Rc::clone(&data);\n let b = Rc::clone(&data);\n\n println!("Referenzzähler: {}", Rc::strong_count(&data)); // 3\n\n drop(a);\n println!("Nach dem Löschen von a: {}", Rc::strong_count(&data)); // 2\n}\n\n\nHier sind data, a und b drei Besitzer eines einzigen Vektors. Solange mindestens einer von ihnen existiert, bleibt der Vektor im Speicher. Dies ist ideal für Situationen, in denen Sie nicht bestimmen können, welches Modul zuletzt beendet wird.
Arc (Atomic Reference Counted) funktioniert genauso, verwendet aber atomare Operationen, was es für Multi-Thread-Programme sicher macht. Der Preis ist eine geringfügige Leistungseinbuße.
use std::sync::Arc;\nuse std::thread;\n\nfn main() {\n let data = Arc::new(vec![1, 2, 3]);\n let mut handles = vec![];\n\n for _ in 0..3 {\n let data_clone = Arc::clone(&data);\n handles.push(thread::spawn(move || {\n println!("{:?}", data_clone);\n }));\n }\n\n for handle in handles {\n handle.join().unwrap();\n }\n}\n\n\n