Закриття
Замикання або лямбда-вирази мають типи, які не можна назвати. Однак вони реалізують спеціальні 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)); }
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(" Грег"); }