ProgrammierungBackend-Entwickler

Was ist der Unterschied zwischen der Verwendung von move und borrow beim Übergeben von Variablen an Funktionen in Rust?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

Wenn eine Variable an eine Funktion übergeben wird, kann sie entweder per Referenz (borrow, mit & oder &mut) oder durch Verschiebung (move, ohne Referenz) übergeben werden.

Borrow: Es wird eine Referenz auf die Daten übergeben. Die Daten bleiben nach dem Funktionsaufruf verfügbar, aber bei einer unveränderlichen Referenz darf der Inhalt nicht geändert werden, für eine veränderliche kann es nur eine aktive Referenz geben.

fn read_length(s: &String) -> usize { s.len() }

Move: Die Variable "zieht" vollständig in die Funktion um. Nach der Übergabe können Sie die ursprüngliche Variable nicht mehr verwenden – sie wurde verschoben, und jeder Zugriff führt zu einem Kompilierungsfehler.

fn destroy(s: String) { println!("{}", s); } // s wird beim Verlassen zerstört let s = String::from("world"); destroy(s); // s kann nicht mehr verwendet werden

Dies verhindert doppelte Speicherfreigaben und andere Besitzfehler.

Fangfrage

Kann ich eine Variable verwenden, nachdem sie per Wertübergabe (move) an eine Funktion übergeben wurde?

Nein! Nach der Übergabe einer Variable durch Wert — z.B. String — wird die ursprüngliche Variable ungültig:

let s = String::from("abc"); consume(s); // s ist hier nicht mehr gültig println!("{}", s); // Kompilierungsfehler

Viele verwechseln dies mit dem Verhalten von Typen mit Copy (z.B. i32), wo die Variable nach der Übergabe weiterhin gültig bleibt.

Beispiele für echte Fehler aufgrund mangelnden Wissens über die Feinheiten des Themas


Geschichte

Ein junger Programmierer schrieb eine Funktion zur Verarbeitung von Strings, die String anstelle von &String akzeptierte. Dadurch wurde der ursprüngliche String nach dem Funktionsaufruf nicht mehr zugänglich, was zu doppelten Beladungen und zusätzlichem Speicherbedarf im Logserver führte.


Geschichte

In einem Mikrodienst wurde eine kritische Ressource über move an verschiedene Threads übergeben, was zu Kompilierungsfehlern beim Zugriff auf diese Ressource im Haupt-Thread führte. Die Architektur musste dringend refaktoriert werden, damit die Ressource über Referenzen oder Wrapper wie Arc übergeben wurde.


Geschichte

Im internen Ereignisparser wurde eine Variable beim Verarbeiten von Daten schnell (move) in ein Closure übernommen, was dazu führte, dass Zugriffe außerhalb des Closures Kompilierungsfehler verursachten. Das Problem wurde erst während des Reviews bemerkt – es wurde ein Stil für die obligatorische Verwendung von borrow für Daten eingeführt, die länger als die lokale Funktion leben sollten.