Что такое замыкание?
Замыкание, также известное как анонимная функция или лямбда-выражение, является функцией, определенной в контексте его использования. Они позволяют определять функции в пределах области действия другой функции и могут захватывать переменные из их окружающей области действия.
В Rust замыкания определяются с помощью пары вертикальных черт (`| |`), которые действуют как скобки функции. Между этими символами задаются аргументы функции, за которыми следует блок кода, составляющий тело функции. Рассмотрим основной пример:
Здесь `add_one` является замыканием, которое принимает один аргумент, `x`, и возвращает `x + 1`.
Захват переменных
Сильной особенностью замыканий является их способность захватывать переменные из окружающей среды, обычно называемая upvar capturing. Переменные могут быть зафиксированы тремя способами:
- By reference: `&T`
- По изменяемой ссылке: `&mut T`
- По значению: `Т`
Rust попытается сделать вывод о наименее ограничительном выборе на основе того, как вы используете переменные в замыкании. Давайте продемонстрируем следующее:
В этом примере заимствование захватывает x по ссылке (`&T`), `borrow_mut` захватывает `x` по изменяемой ссылке (`&mut T`) и перемещает захваченную переменную x по значению (`T`). Ключевое слово `move` используется для принудительной передачи в замыкание во владение значения, которые оно использует.
Вывод типа и дженерики
Реализация замыкания в Rust блещет, когда речь заходит о выводе типа и дженериках. В то время как определения функций требуют явных типов для их аргументов, замыкания Rust не требуют. Кроме того, в отличие от функций, замыкания могут быть дженериками для своих типов ввода. Вот пример:
В этом коде `example_closure` можете использовать строку или `i32`, поскольку он является универсальным.
Fn, FnMut, и FnOnce
В Rust мы имеем три трейта для представления трех видов замыканий: `Fn`, `FnMut` и `FnOnce`, которые соответствуют трем способам захвата переменных:
- `Fn` принимает переменные по ссылке (`&T`)
- `FnMut` принимает переменные по изменяемой ссылке (`&mut T`)
- `FnOnce` принимает переменные по значению (`T`)
Каждое замыкание реализует один или несколько из этих трейтов. Если замыкание реализует `Fn`, то оно также реализует `FnMut` и `FnOnce`. Если оно реализует `FnMut`, оно также реализует `FnOnce`.
Практическое использование замыканий
Существует множество способов использования замыканий в Rust для написания краткого и гибкого кода. Давайте обсудим некоторые из них:
В качестве аргументов функций
Функции в Rust могут принимать замыкания в качестве аргументов. Это часто используется в функциях более высокого порядка, которые принимают другие функции в качестве аргументов или возвращают их в качестве результатов.
Вот пример, где мы используем замыкание для реализации функции `map`, подобной функции `map` на итераторах:
В этом примере `map` принимает `i32`, а замыкание в качестве аргументов применяет замыкание к значению и возвращает результат.
Замыкания в структурах данных
Хранение замыкания в структуре для последующего выполнения является типичным шаблоном в Rust, особенно в конструкциях, управляемых событиями или обратными вызовами.
В этом примере создается структура `Button`, хранящая замыкание как обратный вызов. При нажатии кнопки вызывается обратный вызов.
Замыкания с итераторами
Замыкания широко используются с итераторами в Rust. Они обеспечивают краткий способ выполнения операций над каждым элементом сбора. Например:
В этом примере замыкание `| x | x * x` передается методу `map` итератора, который применяет замыкание к каждому элементу.
В заключение
Замыкание Rust - это захватывающая параметры функция, обеспечивающая гибкость и лаконичность кода. Оно гибко захватывают в свою среду, может использоваться аналогично функциям и приносит такие преимущества, как определение охвата видимости и типа. Оно может использоваться в качестве функциональных параметров, храниться в структурах данных для последующего использования и в паре с итераторами для эффективной обработки данных.
Статья на rusty-code.ru