Learn Rust Coding · Урок

Fn, FnMut, FnOnce

Трейты замыканий

Урок 4 из 413 шагов

«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 — локальная установка не требуется.

Все уроки этого курса

  1. Определение функций
  2. Выражения и инструкции
  3. Замыкания
  4. Fn, FnMut, FnOnce
← Назад к Learn Rust Coding