Закриття

Замикання або лямбда-вирази мають типи, які не можна назвати. Однак вони реалізують спеціальні Fn, FnMut і FnOnce трейти:

fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {
    println!("Виклик функції на {input}");
    func(input)
}

fn main() {
    let add_3 = |x| x + 3;
    println!("add_3: {}", apply_with_log(add_3, 10));
    println!("add_3: {}", apply_with_log(add_3, 20));

    let mut v = Vec::new();
    let mut accumulate = |x: i32| {
        v.push(x);
        v.iter().sum::<i32>()
    };
    println!("accumulate: {}", apply_with_log(&mut accumulate, 4));
    println!("accumulate: {}", apply_with_log(&mut accumulate, 5));

    let multiply_sum = |x| x * v.into_iter().sum::<i32>();
    println!("multiply_sum: {}", apply_with_log(multiply_sum, 3));
}
This slide should take about 10 minutes.

Fn (наприклад, add_3) не споживає і не змінює захоплені значення, або, можливо, не захоплює взагалі нічого. Ії можна викликати кілька разів одночасно.

FnMut (наприклад, accumulate) може змінити захоплені значення. Ви можете викликати ії кілька разів, але не одночасно.

Якщо у вас є FnOnce (наприклад, multiply_sum), ви можете викликати ії лише один раз. Вона може споживати захоплені значення.

FnMut є підтипом FnOnce. Fn є підтипом FnMut і FnOnce. Тобто ви можете використовувати FnMut усюди, де викликається FnOnce, і ви можете використовувати Fn усюди, де викликається FnMut або FnOnce.

Коли ви визначаєте функцію, яка приймає закриття, вам слід використовувати FnOnce, якщо це можливо (тобто ви викликаєте її один раз), або FnMut в іншому випадку, і в останню чергу Fn. Це забезпечує найбільшу гнучкість для того, хто викликає функцію.

На противагу цьому, коли у вас є закриття, найбільш гнучким є Fn (його можна передавати скрізь), потім FnMut і, нарешті, FnOnce.

Компілятор також виводить Copy (наприклад, для add_3) і Clone (наприклад multiply_sum), залежно від того, що захоплює замикання.

За замовчуванням закриття захоплюють за посиланням, якщо це можливо. Ключове слово move змушує їх захоплювати за значенням.

fn make_greeter(prefix: String) -> impl Fn(&str) {
    return move |name| println!("{} {}", prefix, name);
}

fn main() {
    let hi = make_greeter("Привіт".to_string());
    hi(" Грег");
}