Захоплення
Замикання є невід’ємно гнучкими і робитимуть те, що вимагає функціональність, щоб змусити замикання працювати без анотації. Це дозволяє захопленню гнучко пристосовуватися до випадку використання, інколи переміщуючи, а інколи запозичуючи. Замикання можуть захоплювати змінні:
- за посиланням:
&T - за змінним посиланням:
&mut T - за значенням:
T
Вони переважно захоплюють змінні за посиланням і спускаються нижче лише за потреби.
fn main() {
use std::mem;
let color = String::from("green");
// Замикання для друку `color`, яке негайно запозичує (`&`) `color` і
// зберігає запозичення та замикання у змінній `print`. Воно залишатиметься
// запозиченим, доки `print` не буде використано востаннє.
//
// `println!` потребує лише аргументів за незмінним посиланням, тож воно не
// накладає нічого більш обмежувального.
let print = || println!("`color`: {}", color);
// Виклик замикання з використанням запозичення.
print();
// `color` знову можна запозичити незмінно, бо замикання тримає лише
// незмінне посилання на `color`.
let _reborrow = &color;
print();
// Переміщення або повторне запозичення дозволено після фінального використання `print`
let _color_moved = color;
let mut count = 0;
// Замикання для збільшення `count` могло б взяти або `&mut count`, або `count`
// але `&mut count` менш обмежувальне, тож воно його й бере. Негайно
// запозичує `count`.
//
// На `inc` потрібен `mut`, бо всередині зберігається `&mut`. Отже,
// виклик замикання змінює `count`, що потребує `mut`.
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
// Виклик замикання з використанням змінного запозичення.
inc();
// Замикання все ще змінно запозичує `count`, бо його буде викликано пізніше.
// Спроба повторного запозичення призведе до помилки.
// let _reborrow = &count;
// ^ TODO: try uncommenting this line.
inc();
// Тепер замиканню більше не потрібно запозичувати `&mut count`. Тому
// можливо повторно запозичити без помилки
let _count_reborrowed = &mut count;
// Тип без копіювання.
let movable = Box::new(3);
// `mem::drop` потребує `T`, тож це має брати за значенням. Тип із копіюванням
// скопіювався б у замикання, залишивши оригінал недоторканим.
// Тип без копіювання має переміститися, і тому `movable` негайно переміщується в
// замикання.
let consume = || {
println!("`movable`: {:?}", movable);
mem::drop(movable);
};
// `consume` споживає змінну, тож це можна викликати лише один раз.
consume();
// consume();
// ^ TODO: Try uncommenting this line.
}
Використання move перед вертикальними рисками примушує замикання
брати у володіння захоплені змінні:
fn main() {
// `Vec` має семантику без копіювання.
let haystack = vec![1, 2, 3];
let contains = move |needle| haystack.contains(needle);
println!("{}", contains(&1));
println!("{}", contains(&4));
// println!("There're {} elements in vec", haystack.len());
// ^ Uncommenting above line will result in compile-time error
// because borrow checker doesn't allow re-using variable after it
// has been moved.
// Removing `move` from closure's signature will cause closure
// to borrow _haystack_ variable immutably, hence _haystack_ is still
// available and uncommenting above line will not cause an error.
}