Rust 클로저와 제네릭 제약

Beginner

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

소개

이 실습에서는 Rust 의 클로저가 타입 익명성 (type anonymity) 을 사용하며, 클로저를 함수 매개변수로 사용할 때 제네릭 (generics) 을 사용해야 함을 설명합니다.

참고: 실습에서 파일 이름을 지정하지 않으면 원하는 파일 이름을 사용할 수 있습니다. 예를 들어 main.rs 파일을 사용하고 rustc main.rs && ./main 명령어로 컴파일 및 실행할 수 있습니다.

타입 익명성

클로저는 외부 범위의 변수를 간결하게 캡처합니다. 이러한 특징에 어떤 결과가 있을까요? 분명히 있습니다. 클로저를 함수 매개변수로 사용할 때 제네릭 (generics) 이 필요한 이유를 살펴보세요. 이는 클로저의 정의 방식 때문입니다.

// `F` 는 제네릭이어야 합니다.
fn apply<F>(f: F) where
    F: FnOnce() {
    f();
}

클로저가 정의될 때, 컴파일러는 암시적으로 캡처된 변수를 저장하기 위한 새로운 익명 구조체를 생성하고, 이 익명 타입에 대해 Fn, FnMut, 또는 FnOnce 중 하나의 트레이트를 구현합니다. 이 타입은 호출될 때까지 변수에 저장됩니다.

이 새 타입의 타입이 알려지지 않았기 때문에 함수에서 사용하려면 제네릭이 필요합니다. 그러나 제한되지 않은 타입 매개변수 <T>는 여전히 모호하고 허용되지 않습니다. 따라서 Fn, FnMut, 또는 FnOnce (클로저가 구현하는) 중 하나의 트레이트로 제한하는 것이 타입을 명확히 지정하기에 충분합니다.

// `F` 는 인자가 없고 아무것도 반환하지 않는 클로저에 대해 `Fn` 을 구현해야 합니다.
// 이는 `print` 함수에 정확히 필요한 조건입니다.
fn apply<F>(f: F) where
    F: Fn() {
    f();
}

fn main() {
    let x = 7;

    // `x` 를 캡처하여 익명 타입에 저장하고,
    // 해당 타입에 `Fn` 을 구현합니다. `print` 에 저장합니다.
    let print = || println!("{}", x);

    apply(print);
}

요약

축하합니다! 타입 익명성 실습을 완료했습니다. LabEx 에서 더 많은 실습을 통해 기술을 향상시킬 수 있습니다.