Closures
Closures sind anonyme Funktionen, die Variablen aus ihrer Umgebung erfassen können.
Das Erfassen von Variablen aus der Umgebung durch Closures nennt sich auch Currying.
Beispielsweise gibt f
den Wert von x
zurück und hat selbst keine Argumente.
#![allow(unused)] fn main() { let x = vec![0]; let f = || x; assert_eq!(f()[0], 0); }
Wenn wir f
aber ein 2tes Mal aufrufen, bekommen wir einen Fehler.
#![allow(unused)] fn main() { // kompiliert nicht let x = vec![0]; let f = || x; f(); assert_eq!(f()[0], 0); }
Die Rückgabe von x ist ein Move. Den können wir nur einmal ausführen. Daher können wir
f
auch nur einmal aufrufen. Closures, die mindestens einmal aufgerufen werden können, implementieren
den Trait FnOnce
.
fn sort<F>(f: F) -> Vec<i32> where F: FnOnce() -> Vec<i32> { let mut v = f(); v.sort(); v } fn main() { let x = vec![1, 0, 2]; let f = || x; let sorted = sort(f); assert_eq!(sorted, vec![0, 1, 2]); }
Wir klonen den Rückgabewert von f
, damit wir f
mehrfach aufrufen können. Der Trait für beliebig oft
aufrufbare Closures heißt Fn
.
fn sort<F>(f: F) -> Vec<i32> where F: Fn() -> Vec<i32> { f(); let mut v = f(); v.sort(); v } fn main() { let x = vec![1, 0, 2]; let f = || x.clone(); let sorted = sort(f); assert_eq!(sorted, vec![0, 1, 2]); f(); }
Bei der Übergabe an sort
findet ein Move oder eine Kopie von f
und aller erfassten
Variablen von f
statt. Die Variable x
aus der Umgebung wird in diesem Fall per Referenz erfasst.
Da Referenzen kopierbar sind, kann man f
nach Übergabe an sort
erneut ausführen.
Durch Verwendung des Schlüsselworts move
werden die Variablen nicht mehr per Referenz erfasst, sondern
verschoben oder kopiert abhängig vom Vorhandensein einer Copy
-Implementierung.
Mit move
ist ein Aufrufen f
s nach Übergabe an sort
nicht möglich.
// kompiliert nicht fn sort<F>(f: F) -> Vec<i32> where F: Fn() -> Vec<i32> { let mut v = f(); v.sort(); v } fn main() { let x = vec![1, 0, 2]; let f = move || x.clone(); let sorted = sort(f); f(); }
Man kann f
natürlich per Referenz an sort
übergeben.
fn sort<F>(f: &F) -> Vec<i32> where F: Fn() -> Vec<i32> { let mut v = f(); v.sort(); v } fn main() { let x = vec![1, 0, 2]; let f = move || x.clone(); let sorted = sort(&f); assert_eq!(sorted, vec![0, 1, 2]); f(); }
Es gibt auch Closures, die Umgebungsvariablen manipulieren. Diese implementieren FnMut
.
fn sort<F>(f: &mut F) -> Vec<i32> where F: FnMut() -> Vec<i32> { let mut v = f(); v.sort(); v } fn main() { let mut x = vec![1, 0, 2]; let mut f = move || { x[0] += 5; x.clone() }; let sorted = sort(&mut f); assert_eq!(sorted, vec![0, 2, 6]); assert_eq!(f(), vec![11, 0, 2]); }
Quiz
Warum ist das erste Element des Vektors im letzten Assert
11
?Warum würde im obigen Beispiel
fn sort(f: fn() -> Vec<i32>) -> Vec<i32>
nicht funktionieren?
Eine Closure, die Fn
implementiert, implementiert auch FnMut
. Eine Closure die FnMut
implementiert,
implementiert auch FnOnce
.