Как входные параметры
В то время как замыкания Rust выбирают способ захвата переменных на лету, по большей части без указания типов, эта двусмысленность недопустима при написании функций. При использовании замыкания в качестве входного параметра, его тип должен быть указан с использованием одного из типажей. Вот они, в порядке уменьшения ограничений:
Fn
: замыкание захватывает по ссылке (&T
)FnMut
: замыкание захватывает по изменяемой ссылке (&mut T
)FnOnce
: замыкание захватывает по значению (T
)
Компилятор стремится захватывать переменные наименее ограничивающим способом.
Для примера, рассмотрим аргумент, указанный как FnOnce
. Это означает, что
замыкание может захватывать &T
, &mut T
, или T
, но компилятор в итоге
будет выбирать в зависимости от того, как захваченные переменные используются
в замыкании.
Это связано с тем, что если перемещение возможно, тогда любой тип заимствования
также должен быть возможен. Отметим, что обратное не верно. Если параметр
указан как Fn
, то захват переменных как &mut T
или T
недопустим.
В следующем примере попробуйте поменять местами использование Fn
, FnMut
, и
FnOnce
, чтобы увидеть результат:
// Функция, которая принимает замыкание в качестве аргумента и вызывает его. // <F> обозначает, что F - "параметр общего типа" fn apply<F>(f: F) where // Замыкание ничего не принимает и не возвращает. F: FnOnce() { // ^ TODO: Попробуйте изменить это на `Fn` или `FnMut`. f(); } // Функция, которая принимает замыкание и возвращает `i32`. fn apply_to_3<F>(f: F) -> i32 where // Замыкание принимает `i32` и возвращает `i32`. F: Fn(i32) -> i32 { f(3) } fn main() { use std::mem; let greeting = "привет"; // Не копируемый тип. // `to_owned` преобразует заимствованные данные в собственные. let mut farewell = "пока".to_owned(); // Захват двух переменных: `greeting` по ссылке и // `farewell` по значению. let diary = || { // `greeting` захватывается по ссылке: требует `Fn`. println!("Я сказал {}.", greeting); // Изменяемость требует от `farewell` быть захваченным // по изменяемой ссылке. Сейчас требуется `FnMut`. farewell.push_str("!!!"); println!("Потом я закричал {}.", farewell); println!("Теперь я могу поспать. zzzzz"); // Ручной вызов удаления требуется от `farewell` // быть захваченным по значению. Теперь требуется `FnOnce`. mem::drop(farewell); }; // Вызов функции, которая выполняет замыкание. apply(diary); // `double` удовлетворяет ограничениям типажа `apply_to_3` let double = |x| 2 * x; println!("Удвоенное 3: {}", apply_to_3(double)); }