ProgrammierungRust Entwickler

Wie funktionieren Closures in Rust? Welche Typen von Closures gibt es, wie unterscheiden sie sich und wann werden sie verwendet?

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

Antwort

In Rust sind Closures anonyme Funktionen, die Variablen aus dem äußeren Sichtbereich "erfassen" können. Syntax:

let add = |a: i32, b: i32| a + b;

In Rust gibt es drei Arten von Closures (unterscheiden sich in der Art der Variablenübernahme):

  • Fn: erfasst Referenzen (&T), kann mehrfach aufgerufen werden, ohne die Umgebung zu verändern.
  • FnMut: erfasst Referenzen mit Änderungsoption (&mut T), kann Daten innerhalb des Closures bei einem Aufruf ändern.
  • FnOnce: kann nur einmal aufgerufen werden, da es das Eigentum an den erfassten Daten übernimmt (T).

Rust bestimmt automatisch den benötigten Typ, aber Sie können ihn explizit angeben.

Beispiel für Unterschiede:

let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s ist nach dem Aufruf nicht mehr verfügbar

Fangfrage

Warum kann ein Closure mit dem Schlüsselwort move nicht FnOnce sein, sondern als Fn oder FnMut betrachtet werden?

Antwort: Das Schlüsselwort move überträgt die Erfassung von Variablen durch Eigentum, aber wenn die Daten innerhalb des Closures nicht verändert und nicht zerstört werden, bleibt das Closure mit Fn/FnMut kompatibel. Zum Beispiel:

let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();

c kann mehrfach aufgerufen werden: der Wert s wurde in das Closure kopiert, aber nicht zerstört.

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


Geschichte

Ein unerfahrener Rust-Entwickler in einem Datenstromprojekt versuchte, ein Closure ohne move zu schreiben, das in einen anderen Thread übergeben wurde. Infolgedessen beschwerte sich der Compiler über outlived borrow, und er musste sich dringend mit den Feinheiten von lifetime und move befassen.


Geschichte

Mutability wurde in das Closure eingeführt, ohne den Typ auf FnMut zu ändern, was an allen Stellen der Verwendung von Iteratoren "mismatched types" verursachte.


Geschichte

Bei der Verarbeitung eines großen Arrays übernahm das Closure durch move die gesamte Sammlung, ohne es zu merken, und verschob damit das Eigentum, sodass der externe Code nach der ersten Iteration den Zugriff auf die Daten verlor, was zu Versuchen führte, auf den moved value zuzugreifen.