Закриття
Замикання або лямбда-вирази мають типи, які не можна назвати. Однак вони реалізують спеціальні Fn
, FnMut
і FnOnce
трейти:
fn apply_and_log(func: impl FnOnce(i32) -> i32, func_name: &str, input: i32) { println!("Викликаємо {func_name}({input}): {}", func(input)) } fn main() { let n = 3; let add_3 = |x| x + n; apply_and_log(&add_3, "add_3", 10); apply_and_log(&add_3, "add_3", 20); let mut v = Vec::new(); let mut accumulate = |x: i32| { v.push(x); v.iter().sum::<i32>() }; apply_and_log(&mut accumulate, "accumulate", 4); apply_and_log(&mut accumulate, "accumulate", 5); let multiply_sum = |x| x * v.into_iter().sum::<i32>(); apply_and_log(multiply_sum, "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
(яка може бути передана споживачеві будь-якої з 3 трейтів замикання), потім FnMut
і, нарешті, FnOnce
.
Компілятор також виводить Copy
(наприклад, для add_3
) і Clone
(наприклад multiply_sum
), залежно від того, що захоплює замикання. Покажчики функцій (посилання на елементи fn
) реалізують Copy
та Fn
.
За замовчуванням замикання захоплюють кожну змінну із зовнішньої області видимості найменш вибагливою формою доступу (за спільним посиланням, якщо це можливо, потім за ексклюзивним посиланням, потім за переміщенням). Ключове слово move
змушує захоплювати за значенням.
fn make_greeter(prefix: String) -> impl Fn(&str) { return move |name| println!("{} {}", prefix, name); } fn main() { let hi = make_greeter("Привіт".to_string()); hi(" Грег"); }