Fn, FnMut, FnOnce
Трейты замыканий
«Fn, FnMut, FnOnce» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.
Трейты замыканий
Каждое замыкание автоматически реализует один или несколько из трёх трейтов в зависимости от того, как оно использует захваченные значения:
Fn— заимствует неизменяемо.FnMut— заимствует изменяемо.FnOnce— получает владение и может быть вызвано один раз.
fn main() {
let x = 5;
let show = || println!("x is {}", x); // implements Fn
show();
show();
}Fn: неизменяемое заимствование
Замыкание, которое только читает захваченные значения, реализует Fn. Его можно вызывать много раз, ничего не изменяя.
fn main() {
let greeting = String::from("Hi");
let say = || println!("{}", greeting);
say();
say();
}FnMut: изменяемое заимствование
Замыкание, которое изменяет захваченное значение, реализует FnMut. Чтобы вызвать его, сама переменная замыкания должна иметь тип mut.
fn main() {
let mut count = 0;
let mut increment = || {
count += 1;
println!("count {}", count);
};
increment();
increment();
}FnOnce: получение владения
Замыкание, которое перемещает захваченное значение из окружения, реализует FnOnce. Его можно вызвать только один раз.
fn main() {
let name = String::from("Ada");
let consume = move || {
let owned = name;
println!("consumed {}", owned);
};
consume();
}Иерархия трейтов
Трейты образуют иерархию:
- Каждый
Fnтакже являетсяFnMutиFnOnce. - Каждый
FnMutтакже являетсяFnOnce.
Поэтому Fn наиболее гибок, а FnOnce наименее гибок.
fn main() {
let value = 42;
let read = || value; // Fn, so also FnMut and FnOnce
println!("{}", read());
}Передача Fn функциям
Функция может принимать замыкание, указав необходимый трейт. Для этого используйте обобщённые параметры с ограничением трейта.
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(x)
}
fn main() {
let double = |n| n * 2;
println!("{}", apply(double, 9));
}Приём FnMut
Если функции нужно изменять захваченное состояние, укажите требование FnMut и пометьте параметр как mut.
fn call_twice<F: FnMut()>(mut f: F) {
f();
f();
}
fn main() {
let mut total = 0;
call_twice(|| total += 1);
// note: total is borrowed during the call
}Приём FnOnce
Если функция вызывает замыкание только один раз и может позволить ему потребить значения, укажите требование FnOnce.
fn run_once<F: FnOnce() -> String>(f: F) {
let result = f();
println!("{}", result);
}
fn main() {
let s = String::from("done");
run_once(move || s);
}Ключевое слово move
Ключевое слово move заставляет замыкание получить владение захваченными переменными вместо их заимствования.
Это необходимо, когда замыкание должно существовать дольше области видимости, в которой оно создано, например в потоках.
fn main() {
let data = vec![1, 2, 3];
let printer = move || println!("{:?}", data);
printer();
}Как Rust выбирает трейт
Вам не нужно выбирать трейт вручную. Компилятор принимает решение на основе тела замыкания:
- Только читает →
Fn - Изменяет →
FnMut - Перемещает захваченное значение →
FnOnce
fn main() {
let mut log = Vec::new();
let mut record = |x: i32| log.push(x); // FnMut
record(1);
record(2);
println!("{:?}", log);
}Выбор подходящего ограничения
Общее правило:
- Принимайте
Fn, если нужно только вызывать замыкание и читать данные. - Принимайте
FnMut, если замыкание изменяет состояние. - Принимайте
FnOnce, если достаточно одного вызова.
Выбирайте наиболее широкое подходящее ограничение.
fn each<F: Fn(i32)>(items: &[i32], f: F) {
for &i in items {
f(i);
}
}
fn main() {
each(&[1, 2, 3], |n| println!("got {}", n));
}Быстрая проверка
Соотнесите поведение замыкания с его трейтом.
Повторение
Три трейта замыканий:
Fn— заимствует неизменяемо и может вызываться повторно.FnMut— заимствует изменяемо и может изменять состояние.FnOnce— получает владение и может быть вызван один раз.
Компилятор выводит трейт автоматически, а move заставляет захватывать значения во владение.
fn main() {
let base = 5;
let adder = move |x: i32| x + base; // Fn, captures base by move
println!("{}", adder(10));
}Изучай Rust с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 39
- Уроки
- 144
Часто задаваемые вопросы
Урок «Fn, FnMut, FnOnce» бесплатный?
Да — полный текст урока «Fn, FnMut, FnOnce» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.
Чему я научусь в уроке «Fn, FnMut, FnOnce»?
Трейты замыканий Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Fn, FnMut, FnOnce»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Определение функций
- Выражения и инструкции
- Замыкания
- Fn, FnMut, FnOnce