В качестве входных параметров
Хотя Rust обычно выбирает, как захватывать переменные, практически без явной аннотации типа, такая неоднозначность не допускается при написании функций. Когда замыкание передается в качестве входного параметра, его полный тип должен быть аннотирован с использованием одного из нескольких трейтов
, которые определяются тем, как замыкание обрабатывает захваченное значение. В порядке убывания степени ограничения они следующие:
Fn
: замыкание использует захваченное значение по ссылке (&T
)
FnMut
: замыкание использует захваченное значение по изменяемой ссылке (&mut T
)
FnOnce
: замыкание использует захваченное значение по значению (T
)
По отдельности для каждой переменной компилятор будет захватывать переменные в наименее ограничивающем порядке.
Например, рассмотрим параметр, аннотированный как FnOnce
. Это означает, что замыкание может захватывать по &T
, &mut T
или T
, но компилятор в конечном итоге будет выбирать в зависимости от того, как захваченные переменные используются в замыкании.
Это происходит потому, что если возможно совершение move, то и любая другая форма ссылочного доступа должна быть возможной. Обратите внимание, что обратное не верно. Если параметр аннотирован как Fn
, то захват переменных по &mut T
или 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 = "hello";
// Тип, не поддерживающий копирование.
// `to_owned` создает собственное владение над данными из ссылочного доступа
let mut farewell = "goodbye".to_owned();
// Захват двух переменных: `greeting` по ссылке и
// `farewell` по значению.
let diary = || {
// `greeting` по ссылке: требует `Fn`.
println!("I said {}.", greeting);
// Мутация заставляет `farewell` быть захваченным по
// изменяемой ссылке. Теперь требует `FnMut`.
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
println!("Now I can sleep. zzzzz");
// Вручную вызывая drop, заставляем `farewell` быть захваченным по значению. Теперь требует `FnOnce`.
mem::drop(farewell);
};
// Вызываем функцию, которая применяет замыкание.
apply(diary);
// `double` удовлетворяет ограничениям `apply_to_3`
let double = |x| 2 * x;
println!("3 doubled: {}", apply_to_3(double));
}