Как входные параметры

В то время как замыкания 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));
}

Смотрите также:

std::mem::drop, Fn, FnMut, Обобщения, where и FnOnce