Lektionen des Moduls (1/2)
Die Ownership-Regeln
Ownership (Besitz) ist das markanteste Merkmal von Rust. Es ermöglicht der Sprache, die Speichersicherheit zu garantieren, ohne dass ein Garbage Collector benötigt wird oder Sie gezwungen sind, den Heap-Speicher manuell freizugeben (wie in C oder C++).
Die drei Regeln des Ownerships
Die Speicherverwaltung in Rust wird von drei einfachen Regeln beherrscht, die vom Compiler streng überwacht werden:
- Jeder Wert in Rust hat eine Variable, die sein Besitzer (Owner) genannt wird.
- Es kann immer nur einen Besitzer zur gleichen Zeit geben.
- Wenn der Besitzer den Gültigkeitsbereich (Scope) verlässt, wird der Wert automatisch gelöscht.
Stack vs. Heap
Um das Ownership-Konzept zu verstehen, ist es wichtig zu wissen, wo die Daten liegen:
- Stack: Speichert Daten mit einer zur Compilezeit bekannten und festen Größe (z. B. Ganzzahlen, Booleans). Der Zugriff ist extrem schnell.
- Heap: Speichert Daten mit einer zur Compilezeit dynamischen und unbekannten Größe (z. B.
String). Die Allokation ist langsamer und erfordert einen auf dem Stack gespeicherten Zeiger.
Das Konzept des "Move" (Verschiebung)
Wenn wir eine im Heap gespeicherte Variable einer anderen zuweisen, verschiebt (oder überträgt) Rust den Besitz des Wertes und macht die erste Variable ungültig:
let s1 = String::from("ciao");
let s2 = s1; // La proprieta del testo si sposta a s2. s1 NON e piu utilizzabile!
// println!("{}", s1); // ERRORE DI COMPILAZIONE! s1 e "borrow of moved value"
println!("{}", s2); // Valido!
Dieses Verhalten vermeidet das Problem des "Double Free Errors" (der Versuch, denselben Speicher beim Verlassen des Scopes zweimal freizugeben), da nur s2 den Speicher freigibt.
Das Konzept des "Clone" (Tiefenkopie)
Wenn wir den gesamten Inhalt eines String (sowohl die Zeiger auf dem Stack als auch die tatsächlichen Textdaten im Heap) explizit kopieren müssen, können wir auf die Methode .clone() zurückgreifen. Dies dupliziert die Daten im Heap vollständig, wodurch sowohl die ursprüngliche als auch die neue Variable gültig bleiben, sofern wir die Leistungskosten aufgrund der neuen Speicherallokation akzeptieren:
let s1 = String::from("ciao");
let s2 = s1.clone(); // Copia profonda. Entrambe le variabili rimangono valide!
println!("s1: {}, s2: {}", s1, s2);
Das Konzept des "Copy"
Für einfache Typen, die vollständig auf dem Stack gespeichert sind (wie Ganzzahlen i32, Booleans bool, Zeichen char), führt die Zuweisung eine echte automatische flache Kopie durch, sodass beide Variablen gültig bleiben:
let x = 5;
let y = x; // Copia il valore 5 nello stack. Entrambe le variabili sono utilizzabili!
println!("x: {}, y: {}", x, y); // Valido!
Probiere es aus
Deklarieren Sie einen String s1, der den Text 'hello' enthält. Weisen Sie s1 an s2 zu (um den Besitz zu verschieben). Geben Sie s2 schließlich mit println! auf dem Bildschirm aus.
Hinweis anzeigen
Verwenden Sie `let s2 = s1;`, um den Besitz auf `s2` zu übertragen, und geben Sie sie mit `println!('{}', s2);` aus.
Lösung nach 3 Versuchen verfügbar
Deklarieren Sie eine ganzzahlige Variable x mit dem Wert 5. Weisen Sie x an y zu (wodurch sie kopiert wird). Geben Sie schließlich sowohl x als auch y in derselben println!-Anweisung aus.
Hinweis anzeigen
Schreiben Sie `let y = x;` und geben Sie dann beide Werte aus, z. B. `println!('{} {}', x, y);`.
Lösung nach 3 Versuchen verfügbar
Deklarieren Sie einen String s1 mit dem Wert "Rust". Erstellen Sie mithilfe der clone-Methode eine Tiefenkopie von s1 in eine neue Variable s2. Geben Sie schließlich sowohl s1 als auch s2 durch ein Leerzeichen getrennt mit dem println!-Makro aus.
Hinweis anzeigen
Verwenden Sie `let s2 = s1.clone();`, um den Wert von `s1` tief in `s2` zu kopieren, und geben Sie sie dann mit `println!("{} {}", s1, s2);` aus.
Lösung nach 3 Versuchen verfügbar