Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

캡처

클로저는 본질적으로 유연하며, 어노테이션 없이도 클로저가 작동하는 데 필요한 기능을 수행합니다. 이를 통해 캡처 방식이 사용 사례에 따라 유연하게 적응할 수 있으며, 때로는 이동(move)하고 때로는 빌림(borrow)을 수행합니다. 클로저는 다음과 같은 방식으로 변수를 캡처할 수 있습니다:

  • 참조에 의한 방식: &T
  • 가변 참조에 의한 방식: &mut T
  • 값에 의한 방식: T

클로저는 가급적 참조로 변수를 캡처하며, 필요한 경우에만 더 낮은 수준(이동 등)으로 내려갑니다.

fn main() {
    use std::mem;

    let color = String::from("초록");

    // `color`를 출력하는 클로저입니다. 이 클로저는 즉시 `color`를 빌리고(`&`)
// 그 빌림과 클로저를 `print` 변수에 저장합니다. `print`가 마지막으로
// 사용될 때까지 빌린 상태가 유지됩니다.
//
// `println!`은 인자로 불변 참조만 요구하므로 더 이상의 제약을
// 가하지 않습니다.
    let print = || println!("`color`: {}", color);

    // 빌림을 사용하여 클로저를 호출합니다.
    print();

    // 클로저가 `color`에 대한 불변 참조만 가지고 있기 때문에,
// `color`를 다시 불변으로 빌릴 수 있습니다.
    let _reborrow = &color;
    print();

    // `print`를 마지막으로 사용한 후에는 이동(move)이나 다시 빌리는 것이 허용됩니다
    let _color_moved = color;


    let mut count = 0;
    // `count`를 증가시키는 클로저는 `&mut count`나 `count` 중 하나를 취할 수 있지만,
// `&mut count`가 덜 제한적이므로 이를 취합니다. 즉시 `count`를 빌립니다.
//
// 내부에 `&mut`가 저장되어 있기 때문에 `inc`에 `mut`가 필요합니다. 따라서
// 클로저를 호출하면 `count`가 수정되며, 이를 위해 `mut`가 필요합니다.
    let mut inc = || {
        count += 1;
        println!("`count`: {}", count);
    };

    // 가변 빌림을 사용하여 클로저를 호출합니다.
    inc();

    // 클로저가 나중에 호출되기 때문에 여전히 `count`를 가변적으로 빌리고 있습니다.
// 다시 빌리려는 시도는 에러를 발생시킵니다.
// let _reborrow = &count;
// ^ TODO: 이 줄의 주석을 해제해 보세요.
    inc();

    // 클로저가 더 이상 `&mut count`를 빌릴 필요가 없습니다. 따라서
// 에러 없이 다시 빌리는 것이 가능합니다.
    let _count_reborrowed = &mut count;


    // 복사(copy)되지 않는 타입.
    let movable = Box::new(3);

    // `mem::drop`은 `T`를 요구하므로 값을 가져와야 합니다. 복사 타입은
// 원본을 그대로 둔 채 클로저 내부로 복사될 것입니다.
// 복사되지 않는 타입은 이동(move)해야 하므로 `movable`은 즉시
// 클로저 내부로 이동됩니다.
    let consume = || {
        println!("`movable`: {:?}", movable);
        mem::drop(movable);
    };

    // `consume`은 변수를 소비하므로 한 번만 호출될 수 있습니다.
    consume();
    // consume();
// ^ TODO: 이 줄의 주석을 해제해 보세요.
}

수직 바(||) 앞에 move를 사용하면 클로저가 캡처된 변수의 소유권을 강제로 갖게 됩니다:

fn main() {
    // `Vec`은 복사되지 않는 세맨틱을 가집니다.
    let haystack = vec![1, 2, 3];

    let contains = move |needle| haystack.contains(needle);

    println!("{}", contains(&1));
    println!("{}", contains(&4));

    // println!("vec에는 {}개의 요소가 있습니다", haystack.len());
// ^ 위의 줄의 주석을 해제하면 컴파일 타임 에러가 발생합니다.
// 빌림 검사기(borrow checker)가 이동된 후의 변수 재사용을 허용하지 않기 때문입니다.

    // 클로저의 시그니처에서 `move`를 제거하면 클로저가 `haystack` 변수를
// 불변으로 빌리게 되므로, `haystack`을 여전히 사용할 수 있고
// 위의 줄의 주석을 해제해도 에러가 발생하지 않습니다.
}

참고:

Boxstd::mem::drop