В Rust замыкания — это анонимные функции, которые могут "захватывать" переменные из внешней области видимости. Синтаксис:
let add = |a: i32, b: i32| a + b;
В Rust есть три вида замыканий (различаются по способу захвата переменных):
&T), можно вызывать многократно, не мутируя окружение.&mut T), можно изменять данные замыкания при вызове.T).Rust автоматически определяет необходимый тип, но вы можете явно его указать.
Пример различий:
let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s больше недоступен после вызова
Почему closure c ключевым словом move может не быть FnOnce, а являться Fn или FnMut?
Ответ: Ключевое слово move переносит захват переменных по владению, но если данные внутри closure не мутируются и не уничтожаются, closure остаётся совместимым с Fn/FnMut. Например:
let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();
c можно вызвать многократно: значение s скопировано в closure, но не разрушено.
История
Начинающий Rust-разработчик в проекте с потоками данных пытался написать closure без move, передавая его в другой поток. В результате компилятор ругался на outlived borrow, и приходилось срочно разбираться в тонкостях lifetime и move.
История
Ввели mutability внутрь closure, не сменив тип на FnMut, что вызывало "mismatched types" во всех местах использования iterator'ов.
История
При обработке большого массива closure забирал по move всю коллекцию, неосознанно перемещая владение, из-за чего наружный код терял доступ к данным после первой итерации, что привело к попыткам обращаться к moved value.