Lektionen des Moduls (2/2)
Smart Pointers: Box und Rc
Ein Smart Pointer (intelligenter Zeiger) ist eine Datenstruktur, die sich wie ein Zeiger verhält, aber zusätzliche Metadaten und Funktionen besitzt (wie Referenzzählung oder automatische Speicherverwaltung).
In Rust leihen gewöhnliche Referenzen (wie &) nur Daten aus. Smart Pointer hingegen besitzen oft die Daten, auf die sie zeigen. Häufige Beispiele sind String und Vec<T>, aber die Standardbibliothek bietet noch weitere spezialisierte Smart Pointer.
Box<T> zur Allokation im Heap
Der einfachste Smart Pointer ist Box<T>, der Daten im Heap statt auf dem Stack speichert. Auf dem Stack verbleibt nur der Zeiger auf die im Heap allokierten Daten.
Er wird hauptsächlich in folgenden Fällen verwendet:
- Wenn Sie einen Typ haben, dessen Größe zur Compilezeit nicht bekannt sein kann (wie ein rekursiver Typ), und Sie einen Wert dieses Typs in einem Kontext verwenden möchten, der eine exakte Größe erfordert.
- Wenn Sie große Datenmengen übertragen möchten, ohne sie zu kopieren.
Einfaches Beispiel:
fn main() {
let b = Box::new(5); // alloca il valore 5 nello heap
println!("b = {}", *b); // de-referenzia con * per leggere il valore
}
Rekursive Typen und Cons-Listen
Ein rekursiver Typ ist ein Typ, der einen Wert von sich selbst enthalten kann. Rust muss die exakte Größe eines Typs zur Compilezeit kennen. Da die Tiefe eines rekursiven Typs unendlich sein könnte, erzeugt der Compiler einen Fehler, es sei denn, Sie verwenden eine Box (die eine feste Größe hat, da sie ein Zeiger ist):
enum List {
Cons(i32, Box<List>),
Nil,
}
Rc<T>: Referenzzählung (Reference Counting)
Es gibt Fälle, in denen ein einzelner Wert mehrere Besitzer haben kann. Der Smart Pointer Rc<T> (Reference Counted) verfolgt die Anzahl der Referenzen auf einen Wert, um festzustellen, ob der Wert noch verwendet wird. Wenn der Zähler auf Null sinkt, wird die Ressource sicher freigegeben.
[!NOTE]
Rc<T>ist ausschließlich in Single-Thread-Szenarien verwendbar. Für Multi-Thread-Szenarien (konkurrierend) mussArc<T>(Atomic Reference Counted) verwendet werden.
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // incrementa il contatore a 2
println!("Riferimenti: {}", Rc::strong_count(&a));
}
Probiere es aus
Übung 1: Allokieren mit Box
Allokieren Sie einen ganzzahligen Wert von 42 im Heap unter Verwendung von Box::new und weisen Sie ihn einer Variablen namens val zu. Geben Sie anschließend den in der Box gespeicherten Wert aus, indem Sie ihn explizit mit dem Operator * dereferenzieren.
Hinweis anzeigen
Verwenden Sie `let val = Box::new(42);`, um den Wert im Heap zu speichern, und dereferenzieren Sie ihn mit `*val` innerhalb von `println!`.
Lösung nach 3 Versuchen verfügbar
Übung 2: Rekursive Datenstruktur (Cons-Liste)
Definieren Sie ein rekursives Enum namens List, das zwei Varianten enthält: Cons, das ein Tupel (i32, Box<List>) kapselt, und Nil, das das Ende der Liste darstellt. Instanziieren Sie in der main-Methode eine verkettete Liste, die das Element 1 gefolgt von Nil enthält.
Hinweis anzeigen
Das Enum wird als `enum List { Cons(i32, Box<List>), Nil }`deklariert. Instanziieren Sie die Liste mit`List::Cons(1, Box::new(List::Nil))`.
Lösung nach 3 Versuchen verfügbar
Übung 3: Gemeinsamer Besitz mit Rc
Verwenden Sie std::rc::Rc, um eine gemeinsam genutzte Ganzzahl mit dem Wert 100 in einer Variablen a zu erstellen. Klonen Sie die Referenz in eine Variable b unter Verwendung von Rc::clone. Geben Sie schließlich die Anzahl der aktiven Referenzen mit der Funktion Rc::strong_count aus.
Hinweis anzeigen
Erstellen Sie den Wert mit `Rc::new(100)`. Klonen Sie ihn mit `Rc::clone(&a)`und zeigen Sie die Besitzeranzahl mit`Rc::strong_count(&a)` an.
Lösung nach 3 Versuchen verfügbar