Warum Rust sich über Borrow beschwert und wie man es behebt? Vollständige Analyse

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Der Moment, in dem Rust Sie «nicht versteht»

\n

Sie schreiben in Rust. Alles läuft gut. Der Compiler schweigt. Sie fühlen sich wie ein Herrscher über den Speicher. Und dann – peng! – taucht sie auf:

\n
error[E0507]: cannot move out of borrowed content\n  --> src/main.rs:10:9\n   |\n10 |         let value = self.data;\n   |                     ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n
\n

Oder ihre nahe Verwandte:

\n
error[E0507]: cannot move out of `*self` which is behind a shared reference\n
\n

Wenn Sie Anfänger sind, wirkt das wie ein magischer Fluch. Wenn Sie erfahren sind – wissen Sie, dass es nur eine weitere Lektion vom Borrow Checker ist.

\n

In diesem Artikel werden wir analysieren, was diese Fehler bedeuten, warum Rust so streng ist, und – am wichtigsten – wie man sie mit lebendigen Codebeispielen behebt. Dies ist keine langweilige Theorie, sondern ein praktischer Leitfaden zum Überleben in der Welt von Besitz und Ausleihe.

\n\n

Kurz zum Wesentlichen: Besitz, Ausleihe und «Verschiebung»

\n

Bevor wir in die Tiefe gehen, erinnern wir uns an die drei Säulen von Rust:

\n
    \n
  • Besitz (Ownership): Jeder Wert in Rust hat genau einen Besitzer.
  • \n
  • Ausleihe (Borrowing): Sie können einen Wert per Referenz «ausleihen» (&T – unveränderlich, &mut T – veränderlich), ohne den Besitz zu übernehmen.
  • \n
  • Verschiebung (Move): Wenn Sie einen Wert einer neuen Variable zuweisen oder an eine Funktion ohne & übergeben, wird der Besitz verschoben. Die ursprüngliche Variable wird ungültig.
  • \n
\n

Der Fehler cannot move out of borrowed content tritt genau dann auf, wenn Sie versuchen, einen Wert zu verschieben, auf den Sie nur eine Referenz (&T) haben. Rust verbietet dies, weil die Referenz nach der Verschiebung «hängend» (dangling pointer) würde. Und Rust, wie Sie wissen, verzeiht so etwas nicht.

\n\n

Szenario 1: Versuch, ein Strukturfeld über &self zu verschieben

\n

Der häufigste Fall. Sie haben eine Struktur und möchten eines ihrer Felder aus einer Methode zurückgeben, die &self akzeptiert.

\n\n

Code mit Fehler

\n
struct User {\n    name: String,\n    age: u32,\n}\n\nimpl User {\n    // Versuchen, name «endgültig» herauszugeben\n    fn give_name(&self) -> String {\n        self.name // FEHLER!\n    }\n}\n\nfn main() {\n    let user = User { name: "Alice".to_string(), age: 30 };\n    let name = user.give_name();\n    println!("{}", name);\n}\n
\n

Der Compiler wird sagen:

\n
error[E0507]: cannot move out of `self.name` which is behind a shared reference\n
\n

Warum? self ist &User. Wir besitzen self nicht, wir haben es nur ausgeliehen. Der Versuch, self.name (Typ String, der Copy nicht implementiert) zu verschieben, bedeutet, dass der Besitzer (user) nach dem Methodenaufruf sein Feld verlieren würde. Aber die Referenz &self existiert noch! Rust schützt uns vor der Katastrophe.

\n\n

Wie beheben?

\n

Es gibt drei Hauptwege, abhängig von Ihrer Aufgabe.

\n\n

Methode 1: Klonen (am einfachsten, aber nicht immer effizient)

\n

Wenn Sie genau den Wert und keine Referenz benötigen und bereit sind, für das Kopieren zu zahlen:

\n
fn give_name(&self) -> String {\n    self.name.clone() // Funktioniert! Erstellt eine Kopie.\n}\n
\n

Nachteil: clone() für String erstellt eine vollständige Kopie der Daten auf dem Heap. Bei großen Strings ist das teuer.

\n\n

Methode 2: Rückgabe einer Referenz (Signatur ändern)

\n

Wenn der aufrufende Code mit einer Referenz auf das Feld auskommt:

\n
fn give_name(&self) -> &String {\n    &self.name // Geben eine Referenz zurück, keine Verschiebung\n}\n
\n

Jetzt verschieben wir nicht, sondern leihen aus. Alles fair. Der aufrufende Code erhält

Blogs

Buchempfehlungen