ПрограммированиеRust разработчик

Как устроены замыкания (closures) в Rust? Какие существуют типы замыканий, чем они отличаются и когда используются?

Проходите собеседования с ИИ помощником Hintsage

Ответ

В Rust замыкания — это анонимные функции, которые могут "захватывать" переменные из внешней области видимости. Синтаксис:

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

В Rust есть три вида замыканий (различаются по способу захвата переменных):

  • Fn: захватывает ссылки (&T), можно вызывать многократно, не мутируя окружение.
  • FnMut: захватывает ссылки с возможностью изменения (&mut T), можно изменять данные замыкания при вызове.
  • FnOnce: может быть вызван только один раз, так как забирает владение захваченными данными (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.