Einführung
Rust verwendet Ownership, um festzulegen, welcher Teil eines Programms für einen Wert verantwortlich ist und wann dieser Wert bereinigt werden kann. Verstehen Sie Ownership als Verantwortung und Berechtigung und nicht als dauerhaftes Etikett für einen Variablennamen: Diese Verantwortung kann auf ein neues Binding übergehen. Dadurch verhindert Rust mehrere Speicherfehler, ohne dass ein Garbage Collector erforderlich ist.
In diesem Lab sehen Sie zunächst, dass eine kleine Ganzzahl kopiert werden kann. Anschließend weisen Sie Rust absichtlich an, einen eigenen String zu verwenden, nachdem dessen Ownership verschoben wurde. Sie lesen die daraus resultierende Compiler-Diagnose und reparieren anschließend den Datenfluss, indem Sie die von einer Funktion zurückgegebene Ownership verwenden. Das Codegerüst ist vorbereitet, sodass jede Änderung klein und gezielt bleibt.
Gültigkeitsbereich und eine kopierte Ganzzahl beobachten
In diesem Schritt kopieren Sie eine kleine Ganzzahl in einen inneren Gültigkeitsbereich und bestätigen, dass das ursprüngliche Binding weiterhin verwendet werden kann.
Wechseln Sie in das vorbereitete Projekt und öffnen Sie die Quelldatei:
cd /home/labex/project/ownership-demo
nano src/main.rs
Ein Gültigkeitsbereich ist der Bereich, in dem ein Binding gültig ist. Geschweifte Klammern erzeugen einen neuen inneren Gültigkeitsbereich. Das Binding local_count existiert nur zwischen dem inneren Klammernpaar, während warehouse_count zum umgebenden Gültigkeitsbereich von main gehört.
Ersetzen Sie in der Zeile für Schritt 1 ausschließlich 0 durch warehouse_count:
let local_count = warehouse_count; // Step 1: replace the placeholder.
Der Ganzzahltyp i32 implementiert Rusts Copy-Verhalten. Durch die Zuweisung an local_count wird der kleine Wert kopiert, anstatt warehouse_count ungültig zu machen.
Speichern Sie mit Strg+O, drücken Sie Enter und beenden Sie den Editor mit Strg+X. Führen Sie das Programm aus:
cargo run --quiet
Die ersten beiden Zeilen zeigen, dass beide Bindings verwendet werden können:
Local count: 3
Warehouse count: 3
Wenn der innere Gültigkeitsbereich endet, ist local_count nicht mehr gültig. Das äußere Binding warehouse_count bleibt bis zum Ende von main gültig.
Einen absichtlichen Move-Fehler lesen
In diesem Schritt verwenden Sie absichtlich einen String, nachdem dessen Ownership verschoben wurde, und interpretieren anschließend die Compiler-Diagnose von Rust. Die fehlgeschlagene Prüfung ist beabsichtigt und stellt das Lernergebnis dieses Schritts dar.
String::from("PX-104") erstellt einen String mit eigener Ownership. Anders als ein kleiner i32 verwaltet ein String einen separat reservierten Textpuffer, dessen Größe sich ändern kann. Rust überträgt die Verantwortung für diesen Puffer an genau einen Owner, damit er exakt einmal bereinigt werden kann.
Der Aufruf record_parcel(parcel_id) übergibt den String per Wert. Die Ownership wird vom Binding parcel_id auf den Funktionsparameter übertragen. Die Funktion gibt den Wert zurück, und der Aufruf speichert diesen zurückgegebenen Owner in _returned_id. Der führende Unterstrich teilt Rust mit, dass dieses temporäre Binding vorerst absichtlich nicht verwendet wird.
Der Wert wird durch den Move nicht zerstört. Nur das alte Binding verliert die Berechtigung, ihn zu verwenden. Der Ownership-Pfad in diesem Programm lautet:
parcel_id in main → parcel_id parameter in record_parcel → _returned_id in main
Zu jedem Zeitpunkt bleibt genau ein aktueller Owner für denselben Text verantwortlich.
Öffnen Sie die Datei:
nano src/main.rs
Suchen Sie die Zeile unterhalb des Kommentars für Schritt 2 und entfernen Sie nur die führenden //, sodass sie lautet:
println!("Original after move: {parcel_id}");
Speichern Sie die Datei und beenden Sie den Editor. Bevor Sie den nächsten Befehl ausführen, sagen Sie das Ergebnis voraus: parcel_id ist nicht mehr der Owner, daher sollte der Compiler diese Verwendung ablehnen.
Führen Sie die Prüfung aus. Sie soll erwartungsgemäß fehlschlagen:
cargo check
Suchen Sie in der Diagnose nach diesen stabilen Bestandteilen:
error[E0382]: borrow of moved value: `parcel_id`
...
value moved here
...
value borrowed here after move
Das Wort borrow erscheint, weil println! nur vorübergehend auf den Wert zugreifen muss, um ihn auszugeben. Dieser Zugriff über parcel_id ist weiterhin nicht möglich, weil die Ownership bereits verschoben wurde. Im nächsten Schritt verwenden Sie das Binding, das die Ownership von der Funktion zurückerhalten hat.
Mit dem zurückgegebenen Owner fortfahren
In diesem Schritt beheben Sie den Move-Fehler, indem Sie den von record_parcel zurückgegebenen Wert benennen und verwenden.
Die Funktionssignatur fn record_parcel(parcel_id: String) -> String besagt, dass die Funktion die Ownership eines String übernimmt und einen String zurückgibt. Der abschließende Ausdruck parcel_id überträgt die Ownership zurück an den Aufrufer.
Öffnen Sie die Datei:
nano src/main.rs
Nehmen Sie zwei kleine Änderungen vor. Benennen Sie zuerst _returned_id in der Zeile mit dem Funktionsaufruf in returned_id um. Dadurch entfernen Sie den temporären Unterstrich, weil Sie den Wert gleich verwenden werden.
Ersetzen Sie anschließend die fehlschlagende Ausgabezeile durch die folgende Zeile:
println!("Returned owner: {returned_id}");
Das alte Binding parcel_id bleibt nach dem Move ungültig. Durch die Umbenennung von _returned_id in returned_id wird das alte Binding nicht wiederhergestellt. Sie geben lediglich dem Binding, das die Ownership bereits zurückerhalten hat, einen normalen Namen. returned_id ist nun der aktuelle Owner und daher der richtige Name für die Verwendung.
Speichern Sie die Datei, beenden Sie den Editor und prüfen Sie anschließend das reparierte Programm:
cargo check
Ein Ergebnis mit Finished ohne den Fehler E0382 bestätigt, dass der Ownership-Fluss gültig ist. Führen Sie das Programm aus:
cargo run --quiet
Die vollständige Ausgabe sollte lauten:
Local count: 3
Warehouse count: 3
Recorded parcel: PX-104
Returned owner: PX-104
Am Ende von main verlässt returned_id seinen Gültigkeitsbereich, und Rust bereinigt den darin enthaltenen eigenen String. Für diese Daten ist weiterhin genau ein Owner verantwortlich.
Zusammenfassung
Sie haben den lexikalischen Gültigkeitsbereich mit der Lebensdauer eines Werts verknüpft, eine i32-Kopie gesehen, die das ursprüngliche Binding nicht ungültig macht, den Move eines String in eine Funktion verfolgt, E0382 interpretiert und sicher mit der an den Aufrufer zurückgegebenen Ownership weitergearbeitet.



