Введение
В этом лабораторном задании мы исследуем замыкания в Rust и их поведение захвата переменных, которое позволяет им захватывать переменные по ссылке, изменяемой ссылке или значению, в зависимости от требований замыкания.
Примечание: Если в лабораторном задании не указано имя файла, вы можете использовать любое имя файла, которое хотите. Например, вы можете использовать
main.rs, скомпилировать и запустить его с помощьюrustc main.rs &&./main.
Захват переменных
Замыкания по своей природе гибкие и будут делать то, что требует функциональность, чтобы замыкание работало без аннотации. Это позволяет захватывать переменные гибко адаптируясь к конкретному сценарию использования, иногда перемещая, а иногда заимствуя переменные. Замыкания могут захватывать переменные:
- по ссылке:
&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`.
//
// Нужен `mut` для `inc`, потому что внутри хранится `&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!("There're {} elements in vec", haystack.len());
// ^ Раскомментирование строки выше приведет к ошибке компиляции,
// потому что проверщик заимствований не позволяет повторно использовать
// переменную после ее перемещения.
// Удаление `move` из сигнатуры замыкания приведет к тому, что замыкание
// будет заимствовать переменную _haystack_ неизменяемым образом, поэтому
// _haystack_ по-прежнему доступна и раскомментирование строки выше не
// вызовет ошибку.
}
Резюме
Поздравляем! Вы завершили лабораторную работу по захвату переменных. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.