rust closure highorder function HOF

agnusdei·2025년 2월 2일

맞아요! 러스트의 클로저(Closure)고계 함수(Higher-Order Function, HOF)와 밀접한 관계가 있어요.

고계 함수란?

  • 함수를 인자로 받거나 반환하는 함수를 의미해요.
  • 러스트에서는 map, filter, fold 같은 이터레이터 메서드에서 클로저를 자주 사용해요.

클로저란?

  • 클로저는 익명 함수(Anonymous Function)로, 변수를 캡처할 수 있는 함수예요.
  • 일반적인 fn 함수와 달리, 선언된 환경의 변수들을 캡처할 수 있어요.
  • |x| x + 1 같은 간결한 문법으로 사용할 수 있어요.

1. 고계 함수와 클로저의 관계

러스트에서 고계 함수는 보통 클로저를 인자로 받아요. 예제를 볼까요?

fn apply_function<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(x)
}

fn main() {
    let add_one = |x| x + 1;
    let result = apply_function(add_one, 5);
    println!("Result: {}", result); // 6
}

설명

  • apply_function고계 함수예요. (F라는 제네릭 타입을 받아서 함수 f를 실행)
  • add_one = |x| x + 1;클로저 (익명 함수)
  • apply_function(add_one, 5);add_one(5) 실행

즉, 클로저는 고계 함수에서 활용할 수 있는 익명 함수예요!


2. 클로저가 환경을 캡처하는 예제

클로저는 선언된 환경의 변수를 캡처할 수 있어요.

fn main() {
    let factor = 3;
    let multiply = |x| x * factor; // factor를 캡처!
    
    println!("{}", multiply(4)); // 12
}

설명

  • multiply 클로저는 factor 변수를 외부에서 캡처했어요.
  • 일반적인 fn 함수에서는 불가능한 동작이지만, 클로저는 환경을 캡처할 수 있어요.

3. 클로저의 3가지 캡처 방식 (Fn, FnMut, FnOnce)

러스트 클로저는 변수를 캡처할 때 세 가지 방식이 있어요.

트레잇설명
FnOnce한 번만 호출 가능 (소유권 이동)
FnMut변경 가능 (가변 참조)
Fn읽기 전용 (불변 참조)

① FnOnce (소유권 이동)

fn main() {
    let msg = String::from("Hello");
    let consume = || println!("{}", msg); // msg의 소유권을 이동

    consume();
    // consume(); // ❌ ERROR: msg의 소유권이 이동했으므로 재사용 불가
}
  • consume()을 한 번 호출하면 msg의 소유권이 이동해서 다시 사용할 수 없어요.

② FnMut (가변 참조)

fn main() {
    let mut counter = 0;
    let mut increment = || counter += 1; // 가변 참조

    increment();
    increment();
    
    println!("Counter: {}", counter); // 2
}
  • FnMut가변 변수(mut)를 수정할 수 있는 클로저예요.

③ Fn (불변 참조)

fn main() {
    let numbers = vec![1, 2, 3];
    let sum: i32 = numbers.iter().map(|x| x * 2).sum();
    println!("Sum: {}", sum); // 12
}
  • Fn은 단순히 값을 읽기만 해요.
  • numbers.iter().map(|x| x * 2) → 클로저는 x를 읽지만 변경하지 않아요.

4. 클로저 vs 일반 함수

둘 다 함수처럼 동작하지만 차이가 있어요.

특징일반 함수 (fn)클로저 (`
환경 캡처❌ 불가능✅ 가능
타입 추론❌ 필요 (i32 명시)✅ 자동 추론
사용법명확한 시그니처 필요간결한 문법 사용 가능

예제: 함수 vs 클로저

fn add_one_fn(x: i32) -> i32 {
    x + 1
}

fn main() {
    let add_one_closure = |x| x + 1; // 클로저

    println!("{}", add_one_fn(5));       // 6
    println!("{}", add_one_closure(5));  // 6
}
  • fn 함수는 명확한 시그니처 필요 (i32 명시)
  • 클로저는 타입 추론 가능 (|x| x + 1만 써도 됨)

5. 클로저를 활용한 고계 함수 예제

러스트에서 클로저는 이터레이터 API와 함께 자주 사용돼요.

예제: filtermap을 활용한 클로저

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    let even_numbers: Vec<i32> = numbers.iter()
        .filter(|&x| x % 2 == 0) // 클로저 사용
        .map(|x| x * 2)
        .collect();

    println!("{:?}", even_numbers); // [4, 8]
}
  • .filter(|&x| x % 2 == 0) → 짝수만 필터링
  • .map(|x| x * 2) → 각 숫자를 2배로 변환

결론

  1. 클로저는 고계 함수에서 활용할 수 있는 익명 함수
  2. 환경을 캡처할 수 있으며 (Fn, FnMut, FnOnce)
  3. 이터레이터 API와 함께 자주 사용됨

Rust에서 함수형 프로그래밍 스타일을 활용하고 싶다면 클로저와 고계 함수를 잘 이해하는 것이 중요해요!

profile
DevSecOps, Pentest, Cloud(OpenStack), Develop, Data Engineering, AI-Agent

0개의 댓글