let movable = Box::new(3);
// `mem::drop` требует `T`, так что захват производится по значению.
// Копируемый тип будет скопирован в замыкание, оставив оригинальное
// значение без изменения. Некопируемый тип должен быть перемещён, так что
// movable` немедленно перемещается в замыкание.
let consume = || {
println!("`movable`: {:?}", movable);
mem::drop(movable);
};
// `consume` поглощает переменную, так что оно может быть вызвано только один раз.
consume();
// consume();
// ^ TODO: Попробуйте раскомментировать эту строку.
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Использование 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_ будет заимствован по неизменяемой
// ссылке, и удалённый комментарий теперь не вызывает ошибки.
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Смотрите также:
Box и std::mem::drop
Как входные параметры
В то время как замыкания Rust выбирают способ захвата переменных на лету, по большей части без указания типов, эта двусмысленность недопустима при написании функций. При использовании замыкания в качестве входного параметра, его тип должен быть указан с использованием одного из типажей. Вот они, в порядке уменьшения ограничений:
• Fn: замыкание захватывает по ссылке (&T)
• FnMut: замыкание захватывает по изменяемой ссылке (&mut T)
• FnOnce: замыкание захватывает по значению (T)
Компилятор стремится захватывать переменные наименее ограничивающим способом.
Для примера, рассмотрим аргумент, указанный как FnOnce. Это означает, что замыкание может захватывать &T, &mut T, или T, но компилятор в итоге будет выбирать в зависимости от того, как захваченные переменные используются в замыкании.
Это связано с тем, что если перемещение возможно, тогда любой тип заимствования также должен быть возможен. Отметим, что обратное не верно. Если параметр указан как Fn, то захват переменных как &mut T или T недопустим.
В следующем примере попробуйте поменять местами использование Fn, FnMut, и FnOnce, чтобы увидеть результат:
// Функция, которая принимает замыкание в качестве аргумента и вызывает его.
// обозначает, что F - "параметр общего типа"
fn apply(f: F) where
// Замыкание ничего не принимает и не возвращает.
F: FnOnce() {
// ^ TODO: Попробуйте изменить это на `Fn` или `FnMut`.
f();
}
// Функция, которая принимает замыкание и возвращает `i32`.
fn apply_to_3(f: F) -> i32 where
// Замыкание принимает `i32` и возвращает `i32`.
F: Fn(i32) -> i32 {
f(3)
}
fn main() {
use std::mem;
let greeting = "привет";
// Не копируемый тип.
// `to_owned` преобразует заимствованные данные в собственные.
let mut farewell = "пока".to_owned();
// Захват двух переменных: `greeting` по ссылке и
// `farewell` по значению.
let diary = || {
// `greeting` захватывается по ссылке: требует `Fn`.
println!("Я сказал {}.", greeting);
// Изменяемость требует от `farewell` быть захваченным
// по изменяемой ссылке. Сейчас требуется `FnMut`.
farewell.push_str("!!!");
println!("Потом я закричал {}.", farewell);
println!("Теперь я могу поспать. zzzzz");
// Ручной вызов удаления требуется от `farewell`
// быть захваченным по значению. Теперь требуется `FnOnce`.
mem::drop(farewell);
};
// Вызов функции, которая выполняет замыкание.
apply(diary);
// `double` удовлетворяет ограничениям типажа `apply_to_3`
let double = |x| 2 * x;
println!("Удвоенное 3: {}", apply_to_3(double));
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Смотрите также:
std::mem::drop, Fn, FnMut, Обобщения, where и FnOnce
Анонимность типов
Замыкания временно захватывают переменные из окружающих областей видимости. Имеет ли это какие-либо последствия? Конечно. Как видите, использование замыкания в аргументах функции требует обобщённых типов из-за особенностей реализации замыканий:
#![allow(unused)]
fn main() {
// `F` должен быть обобщённым типом.
fn apply(f: F) where
F: FnOnce() {
f();
}
}
Во время определения замыкания компилятор неявно создаёт новую анонимную структуру для хранения захваченных переменных, тем временем реализуя функциональность для некого неизвестного типа с помощью одного из типажей: Fn, FnMut, или FnOnce. Этот тип присваивается переменной, которая хранится до самого вызова замыкания.