입력 매개변수로서의 클로저

Beginner

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

Introduction

In this lab, we learn that when writing functions in Rust that take a closure as an input parameter, the closure's complete type must be annotated using one of the traits: Fn, FnMut, or FnOnce, which determine how the closure uses the captured value, either by reference, mutable reference, or value. The compiler captures variables in the least restrictive manner possible based on the chosen trait for the closure.

Note: If the lab does not specify a file name, you can use any file name you want. For example, you can use main.rs, compile and run it with rustc main.rs && ./main.

입력 매개변수로서의 클로저

Rust 는 대개 타입 주석 없이 변수를 캡처하는 방법을 자동으로 결정하지만, 함수를 작성할 때는 이러한 모호성을 허용하지 않습니다. 클로저를 입력 매개변수로 받을 때는 클로저의 전체 타입을 몇 가지 트레이트 중 하나를 사용하여 주석으로 표시해야 합니다. 이 트레이트는 클로저가 캡처한 값을 참조, 가변 참조 또는 값으로 사용하는 방식에 따라 결정됩니다. 제약 조건이 강한 순서대로 다음과 같습니다.

  • Fn: 클로저가 캡처한 값을 참조 (&T) 로 사용합니다.
  • FnMut: 클로저가 캡처한 값을 가변 참조 (&mut T) 로 사용합니다.
  • FnOnce: 클로저가 캡처한 값을 값 (T) 으로 사용합니다.

변수별로 컴파일러는 가능한 가장 제약이 적은 방식으로 변수를 캡처합니다.

예를 들어, 매개변수가 FnOnce로 주석 처리된 경우, 클로저는 &T, &mut T 또는 T로 캡처할 수 있지만, 컴파일러는 클로저 내에서 캡처된 변수가 사용되는 방식에 따라 최종적으로 선택합니다.

이는 이동이 가능하면 모든 종류의 빌드도 가능해야 하기 때문입니다. 반대의 경우는 사실이 아닙니다. 매개변수가 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();

    // 2 개의 변수를 캡처합니다: `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 에서 더 많은 실습을 통해 기술을 향상시킬 수 있습니다.