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 fs 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.