W Rust zamknięcia to anonimowe funkcje, które mogą "przechwytywać" zmienne z zewnętrznego zakresu. Składnia:
let add = |a: i32, b: i32| a + b;
W Rust istnieją trzy rodzaje zamknięć (różnią się sposobem przechwytywania zmiennych):
&T), można wywoływać wielokrotnie, nie mutując otoczenia.&mut T), można zmieniać dane zamknięcia przy wywołaniu.T).Rust automatycznie określa wymagany typ, ale można go wyraźnie wskazać.
Przykład różnic:
let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s nie jest już dostępne po wywołaniu
Dlaczego zamknięcie z kluczem move może nie być FnOnce, a być Fn lub FnMut?
Odpowiedź: Kluczowe słowo move przenosi przechwytywanie zmiennych przez własność, ale jeśli dane wewnątrz zamknięcia nie są mutowane i nie są usuwane, zamknięcie pozostaje zgodne z Fn/FnMut. Na przykład:
let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();
c można wywołać wielokrotnie: wartość s została skopiowana do zamknięcia, ale nie została zniszczona.
Historia
Początkujący programista Rust w projekcie z przepływem danych próbował napisać zamknięcie bez move, przekazując je do innego wątku. W rezultacie kompilator zgłaszał błąd outlived borrow, co wymuszało nagłe zgłębianie szczegółów lifetime i move.
Historia
Wprowadzono mutability do zamknięcia, nie zmieniając typu na FnMut, co powodowało "mismatched types" w każdym miejscu użycia iteratorów.
Historia
Podczas przetwarzania dużej tablicy zamknięcie przejmowało przez move całą kolekcję, nieświadomie przenosząc własność, przez co zewnętrzny kod tracił dostęp do danych po pierwszej iteracji, co prowadziło do prób uzyskania dostępu do moved value.