В качестве входных параметров

RustRustBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии мы узнаем, что при написании функций на Rust, которые принимают замыкание в качестве входного параметра, полный тип замыкания должен быть аннотирован с использованием одного из трейтов: Fn, FnMut или FnOnce, которые определяют, как замыкание использует захваченное значение, по ссылке, по изменяемой ссылке или по значению. Компилятор захватывает переменные в наименее ограничивающем порядке, исходя из выбранного для замыкания трейта.

Примечание: Если практическое занятие не задает имя файла, вы можете использовать любое имя файла, которое хотите. Например, вы можете использовать main.rs, скомпилировать и запустить его с помощью rustc main.rs &&./main.

В качестве входных параметров

Хотя Rust обычно выбирает, как захватывать переменные, практически без явной аннотации типа, такая неоднозначность не допускается при написании функций. Когда замыкание передается в качестве входного параметра, его полный тип должен быть аннотирован с использованием одного из нескольких трейтов, которые определяются тем, как замыкание обрабатывает захваченное значение. В порядке убывания степени ограничения они следующие:

  • Fn: замыкание использует захваченное значение по ссылке (&T)
  • FnMut: замыкание использует захваченное значение по изменяемой ссылке (&mut T)
  • FnOnce: замыкание использует захваченное значение по значению (T)

По отдельности для каждой переменной компилятор будет захватывать переменные в наименее ограничивающем порядке.

Например, рассмотрим параметр, аннотированный как FnOnce. Это означает, что замыкание может захватывать по &T, &mut T или T, но компилятор в конечном итоге будет выбирать в зависимости от того, как захваченные переменные используются в замыкании.

Это происходит потому, что если возможно совершение move, то и любая другая форма ссылочного доступа должна быть возможной. Обратите внимание, что обратное не верно. Если параметр аннотирован как Fn, то захват переменных по &mut T или T не допускается. Однако, захват по &T допустим.

В следующем примере попробуйте поменять местами использование Fn, FnMut и FnOnce, чтобы увидеть, что произойдет:

// Функция, которая принимает замыкание в качестве аргумента и вызывает его.
// <F> обозначает, что F - "обобщенный тип параметра"
fn apply<F>(f: F) where
    // Замыкание не имеет входных параметров и не возвращает ничего.
    F: FnOnce() {
    // ^ TODO: Попробуйте изменить это на `Fn` или `FnMut`.

    f();
}

// Функция, которая принимает замыкание и возвращает `i32`.
fn apply_to_3<F>(f: F) -> i32 where
    // Замыкание принимает `i32` и возвращает `i32`.
    F: Fn(i32) -> i32 {

    f(3)
}

fn main() {
    use std::mem;

    let greeting = "hello";
    // Тип, не поддерживающий копирование.
    // `to_owned` создает собственное владение над данными из ссылочного доступа
    let mut farewell = "goodbye".to_owned();

    // Захват двух переменных: `greeting` по ссылке и
    // `farewell` по значению.
    let diary = || {
        // `greeting` по ссылке: требует `Fn`.
        println!("I said {}.", greeting);

        // Мутация заставляет `farewell` быть захваченным по
        // изменяемой ссылке. Теперь требует `FnMut`.
        farewell.push_str("!!!");
        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzz");

        // Вручную вызывая drop, заставляем `farewell` быть захваченным по значению. Теперь требует `FnOnce`.
        mem::drop(farewell);
    };

    // Вызываем функцию, которая применяет замыкание.
    apply(diary);

    // `double` удовлетворяет ограничениям `apply_to_3`
    let double = |x| 2 * x;

    println!("3 doubled: {}", apply_to_3(double));
}

Резюме

Поздравляем! Вы завершили практическое занятие "В качестве входных параметров". Вы можете выполнить больше практических занятий в LabEx, чтобы улучшить свои навыки.