Захват
Замыкания довольно гибкие и делают всё, что требуется, для работы с ними без дополнительных указаний. Это позволяет захватывать переменные гибко, перемещая их или заимствуя, в зависимости от ситуации. Замыкания могут захватывать переменные:
- по ссылке:
&T
- по изменяемой ссылке:
&mut T
- по значению:
T
Преимущественно, они захватывают переменные по ссылке, и используют другие способы только там, где это необходимо.
fn main() { use std::mem; let color = "green"; // Замыкание для вывода `color`, которое немедленно заимствует (`&`) // `color` и сохраняет замыкание в переменной `print`. `color` будет оставаться // заимствованным до тех пор, пока `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`. // Таким образом, вызов замыкания изменяет его, что недопустимо без `mut`. let mut inc = || { count += 1; println!("`count`: {}", count); }; // Вызываем замыкание, использующее изменяемое заимствование. inc(); // Замыкание продолжает изменяемо заимствовать `count`, так как оно используется дальше. // Попытка перезанять приведёт к ошибке. // let _reborrow = &count; // ^ TODO: попробуйте раскомментировать эту строку. 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: Попробуйте раскомментировать эту строку. }
Использование move
перед вертикальными линиями позволяет получить владение над захваченными переменными:
fn main() { // Vec` не поддерживает копирование. let haystack = vec![1, 2, 3]; let contains = move |needle| haystack.contains(needle); println!("{}", contains(&1)); println!("{}", contains(&4)); // println!("Количество элементов {} в векторе", haystack.len()); // ^ Уберите комментарий с этой строки и в результате получите ошибку компиляции, // потому что анализатор заимствований не позволяет использовать // переменную после передачи владения. // Удалите `move` у замыкания и _haystack_ будет заимствован по неизменяемой // ссылке, и удалённый комментарий теперь не вызывает ошибки. }