클로저 이해하기

토파즈에서 클로저의 강력함을 발견하세요. 기본 개념부터 고급 패턴, 실용적인 활용법까지 완전 가이드입니다.

클로저는 함수형 프로그래밍의 핵심이자 가장 우아한 개념 중 하나입니다. 토파즈에서 클로저의 마법을 경험해보세요! ✨

🌱 클로저의 기본 개념

클로저란 무엇인가?

클로저는 자신이 정의된 환경의 변수들을 기억하고 접근할 수 있는 함수입니다. 함수가 생성될 때의 환경(스코프)을 "닫아서(close)" 가지고 다니기 때문에 클로저라고 불립니다.

// 기본 클로저 예제
function 외부함수(외부변수: int) -> impl Fn(int) -> int {
    // 클로저 생성 - 외부변수를 캡처함
    let 내부함수 = |내부변수: int| -> int {
        외부변수 + 내부변수  // 외부 스코프의 변수에 접근
    }
    
    return 내부함수
}

// 클로저 카운터 예제
function 카운터생성기(초기값: int) -> impl Fn() -> int {
    let mut 현재값 = 초기값
    
    // 현재값을 캡처하는 클로저
    return move || -> int {
        현재값 += 1
        현재값
    }
}

// 곱하기 함수 생성기
function 곱하기생성기(배수: int) -> impl Fn(int) -> int {
    |: int|* 배수
} test {
    // 외부함수 테스트
    let 더하기5 = 외부함수(5)
    assert 더하기5(3) == 8
    assert 더하기5(10) == 15
    
    // 카운터 테스트
    let mut 카운터1 = 카운터생성기(0)
    let mut 카운터2 = 카운터생성기(100)
    
    assert 카운터1() == 1
    assert 카운터1() == 2
    assert 카운터2() == 101
    assert 카운터1() == 3  // 독립적인 상태 유지
    
    // 곱하기 생성기 테스트
    let 두배만들기 = 곱하기생성기(2)
    let 세배만들기 = 곱하기생성기(3)
    
    assert 두배만들기(5) == 10
    assert 세배만들기(4) == 12
}

print("더하기5(7) = {외부함수(5)(7)}")

let mut 내카운터 = 카운터생성기(10)
print("카운터: {내카운터()}, {내카운터()}, {내카운터()}")

let 열배만들기 = 곱하기생성기(10)
print("10 * 7 = {열배만들기(7)}")

클로저의 핵심 특징

  1. 환경 캡처: 외부 스코프의 변수들을 기억
  2. 상태 보존: 함수 호출 사이에 상태를 유지
  3. 지연 실행: 필요할 때까지 계산을 미룸
  4. 코드 재사용: 동일한 로직을 다양한 컨텍스트에서 활용

🎨 다양한 클로저 패턴

값 캡처 vs 참조 캡처

// 값 캡처 (Copy)
function 값캡처예제() -> impl Fn() -> int {
    let= 42
    
    // 값을 복사해서 캡처
    ||// 값이 클로저 내부로 복사됨
}

// 참조 캡처 (Borrow)
function 참조캡처예제() -> impl Fn() -> int {
    let= 42
    
    // 참조로 캡처 (수명이 허용하는 경우)
    move ||// move 키워드로 소유권 이전
}

// 가변 참조 캡처
function 가변캡처예제() -> impl FnMut() -> int {
    let mut 카운트 = 0
    
    // 가변 참조로 캡처
    move || -> int {
        카운트 += 1
        카운트
    }
}

// 복잡한 환경 캡처
function 복잡한환경() -> (impl Fn(int) -> int, impl Fn(int) -> int) {
    let 기본값 = 10
    let 배수 = 3
    let mut 누적값 = 0
    
    let 더하기함수 = move |추가값: int| -> int {
        누적값 += 추가값
        기본값 + 누적값
    }
    
    let 곱하기함수 = |입력값: int| -> int {
        입력값 * 배수
    }
    
    return (더하기함수, 곱하기함수)
}

// 클로저 체이닝
function 함수체인생성(초기함수: impl Fn(int) -> int) -> impl Fn(impl Fn(int) -> int) -> impl Fn(int) -> int {
    move |다음함수: impl Fn(int) -> int| -> impl Fn(int) -> int {
        move |입력: int| -> int {
            다음함수(초기함수(입력))
        }
    }
} test {
    // 값 캡처 테스트
    let 값클로저 = 값캡처예제()
    assert 값클로저() == 42
    
    // 가변 캡처 테스트  
    let mut 가변클로저 = 가변캡처예제()
    assert 가변클로저() == 1
    assert 가변클로저() == 2
    assert 가변클로저() == 3
    
    // 복잡한 환경 테스트
    let (mut 더하기, 곱하기) = 복잡한환경()
    assert 더하기(5) == 15  // 10 + 5
    assert 더하기(3) == 18  // 10 + 5 + 3
    assert 곱하기(4) == 12   // 4 * 3
    
    // 함수 체이닝 테스트
    let 두배 = |x: int| x * 2
    let 더하기10 = |x: int| x + 10
    
    let 체인드함수 = 함수체인생성(두배)(더하기10)
    assert 체인드함수(5) == 20  // (5 * 2) + 10
}

let mut 내가변클로저 = 가변캡처예제()
print("가변 클로저: {내가변클로저()}, {내가변클로저()}")

let (mut 더하기함수, 곱하기함수) = 복잡한환경()
print("더하기: {더하기함수(7)}, 곱하기: {곱하기함수(6)}")

고차 함수와 클로저

// 함수를 반환하는 함수
function 조건부함수생성기(조건: bool) -> impl Fn(int) -> int {
    if 조건 {
        |x: int| x * 2  // 두 배
    } else {
        |x: int| x + 10  // 10 더하기
    }
}

// 함수를 매개변수로 받는 함수
function 함수적용기<F, T>(값: T, 함수: F) -> T 
where
    F: Fn(T) -> T
{
    함수(값)
}

// 여러 함수를 조합하는 컴포지터
function 함수컴포지터<F, G, T>(함수1: F, 함수2: G) -> impl Fn(T) -> T
where
    F: Fn(T) -> T,
    G: Fn(T) -> T,
    T: 'static
{
    move |입력: T| -> T {
        함수2(함수1(입력))
    }
}

// 커링(Currying) 구현
function 커링예제(a: int) -> impl Fn(int) -> impl Fn(int) -> int {
    move |b: int| -> impl Fn(int) -> int {
        move |c: int| -> int {
            a + b + c
        }
    }
}

// 메모이제이션 클로저
function 메모이제이션<F, T, U>(함수: F) -> impl FnMut(T) -> U
where
    F: Fn(T) -> U,
    T: Clone + std::hash::Hash + Eq,
    U: Clone,
{
    use std::collections::HashMap
    
    let mut 캐시: HashMap<T, U> = HashMap::new()
    
    move |입력: T| -> U {
        match 캐시.get(&입력) { case Some(결과) => return 결과.clone(), case None => {} }
        
        let 결과 = 함수(입력.clone())
        캐시.insert(입력, 결과.clone())
        결과
    }
} test {
    // 조건부 함수 생성기 테스트
    let 두배함수 = 조건부함수생성기(true)
    let 더하기함수 = 조건부함수생성기(false)
    
    assert 두배함수(5) == 10
    assert 더하기함수(5) == 15
    
    // 함수 적용기 테스트
    let 결과 = 함수적용기(10, |x| x * 3)
    assert 결과 == 30
    
    // 함수 컴포지터 테스트
    let 더하기5 = |x: int| x + 5
    let 곱하기2 = |x: int| x * 2
    let 조합함수 = 함수컴포지터(더하기5, 곱하기2)
    
    assert 조합함수(3) == 16  // (3 + 5) * 2
    
    // 커링 테스트
    let 커링함수 = 커링예제(1)(2)(3)
    assert 커링함수 == 6  // 1 + 2 + 3
    
    // 메모이제이션 테스트
    let mut 메모팩토리얼 = 메모이제이션(|n: int| -> int {
        if n <= 1 { 1 } else { n * 메모팩토리얼(n - 1) }
    })
    
    assert 메모팩토리얼(5) == 120
}

print("조건부(true): {조건부함수생성기(true)(7)}")
print("조합함수 결과: {함수컴포지터(|x| x + 1, |x| x * 10)(4)}")
print("커링 결과: {커링예제(10)(20)(30)}")

이벤트 처리와 콜백

// 이벤트 핸들러 타입 정의
type 이벤트핸들러<T> = Box<dyn FnMut(T)>

// 간단한 이벤트 시스템
struct 이벤트시스템<T> {
    핸들러들: Vec<이벤트핸들러<T>>
}

impl<T> 이벤트시스템<T>
where
    T: Clone
{
    function new() -> Self {
        이벤트시스템 {
            핸들러들: Vec::new()
        }
    }
    
    // 이벤트 핸들러 등록
    function 핸들러등록<F>(&mut self, 핸들러: F)
    where
        F: FnMut(T) + 'static
    {
        self.핸들러들.push(Box::new(핸들러))
    }
    
    // 이벤트 발생
    function 이벤트발생(&mut self, 데이터: T) {
        for 핸들러 in &mut self.핸들러들 {
            핸들러(데이터.clone())
        }
    }
}

// 비동기 작업 시뮬레이션
function 비동기작업<F>(지연시간: int, 완료콜백: F)
where
    F: FnOnce(string) + 'static
{
    // 실제로는 비동기 처리가 되겠지만, 여기서는 즉시 실행
    let 결과 = format!("작업 완료 ({}ms 후)", 지연시간)
    완료콜백(결과)
}

// 상태 변화 감지기
struct 상태감지기<T> {
    현재상태: T,
    변화콜백들: Vec<Box<dyn Fn(&T, &T)>>
}

impl<T> 상태감지기<T>
where
    T: Clone + PartialEq
{
    function new(초기상태: T) -> Self {
        상태감지기 {
            현재상태: 초기상태,
            변화콜백들: Vec::new()
        }
    }
    
    function 변화감지등록<F>(&mut self, 콜백: F)
    where
        F: Fn(&T, &T) + 'static
    {
        self.변화콜백들.push(Box::new(콜백))
    }
    
    function 상태변경(&mut self, 새상태: T) {
        if self.현재상태 != 새상태 {
            let 이전상태 = self.현재상태.clone()
            self.현재상태 = 새상태.clone()
            
            for 콜백 in &self.변화콜백들 {
                콜백(&이전상태, &새상태)
            }
        }
    }
    
    function 현재값(&self) -> &T {
        &self.현재상태
    }
} test {
    // 이벤트 시스템 테스트
    let mut 이벤트시스템 = 이벤트시스템::<string>::new()
    
    let mut 받은메시지들: Vec<string> = Vec::new()
    
    이벤트시스템.핸들러등록(move |메시지: string| {
        받은메시지들.push(format!("핸들러1: {}", 메시지))
    })
    
    이벤트시스템.핸들러등록(|메시지: string| {
        println!("핸들러2에서 받음: {}", 메시지)
    })
    
    이벤트시스템.이벤트발생("테스트 메시지".to_string())
    
    // 상태 감지기 테스트
    let mut 온도감지기 = 상태감지기::new(20.0)
    
    let mut 변화기록: Vec<string> = Vec::new()
    
    온도감지기.변화감지등록(move |이전: &f64, 새값: &f64| {
        변화기록.push(format!("온도 변화: {}°C → {}°C", 이전, 새값))
    })
    
    온도감지기.상태변경(25.0)
    온도감지기.상태변경(30.0)
    
    assert *온도감지기.현재값() == 30.0
}

// 사용 예시
let mut 클릭이벤트 = 이벤트시스템::<string>::new()

클릭이벤트.핸들러등록(|버튼명: string| {
    print!("버튼 '{}'이 클릭되었습니다!", 버튼명)
})

클릭이벤트.이벤트발생("확인".to_string())

// 비동기 작업 예시
비동기작업(1000, |결과: string| {
    print!("비동기 작업 결과: {}", 결과)
})

🚀 실용적인 클로저 활용

함수형 프로그래밍 패턴

// 함수형 파이프라인
function 파이프라인<T>(초기값: T) -> 파이프라인빌더<T> {
    파이프라인빌더::new(초기값)
}

struct 파이프라인빌더<T> {
: T
}

impl<T> 파이프라인빌더<T> {
    function new(값: T) -> Self {
        파이프라인빌더 { 값 }
    }
    
    function map<U, F>(self, 함수: F) -> 파이프라인빌더<U>
    where
        F: FnOnce(T) -> U
    {
        파이프라인빌더::new(함수(self.값))
    }
    
    function filter<F>(self, 조건: F) -> Option<파이프라인빌더<T>>
    where
        F: FnOnce(&T) -> bool
    {
        if 조건(&self.값) {
            Some(self)
        } else {
            None
        }
    }
    
    function fold<U, F>(self, 초기값: U, 함수: F) -> U
    where
        F: FnOnce(U, T) -> U
    {
        함수(초기값, self.값)
    }
    
    function finish(self) -> T {
        self.
    }
}

// 배열 처리 유틸리티
function 배열처리<T>() -> 배열처리기<T> {
    배열처리기::new()
}

struct 배열처리기<T> {
    _phantom: std::marker::PhantomData<T>
}

impl<T> 배열처리기<T> {
    function new() -> Self {
        배열처리기 { _phantom: std::marker::PhantomData }
    }
    
    function map<U, F>(&self, 배열: Vec<T>, 함수: F) -> Vec<U>
    where
        F: Fn(T) -> U
    {
        배열.into_iter().map(함수).collect()
    }
    
    function filter<F>(&self, 배열: Vec<T>, 조건: F) -> Vec<T>
    where
        F: Fn(&T) -> bool
    {
        배열.into_iter().filter(조건).collect()
    }
    
    function reduce<F>(&self, 배열: Vec<T>, 함수: F) -> Option<T>
    where
        F: Fn(T, T) -> T
    {
        배열.into_iter().reduce(함수)
    }
    
    function fold<U, F>(&self, 배열: Vec<T>, 초기값: U, 함수: F) -> U
    where
        F: Fn(U, T) -> U
    {
        배열.into_iter().fold(초기값, 함수)
    }
}

// 조건부 실행 체인
function 만약<F>(조건: bool, 실행함수: F) -> 조건부실행<F>
where
    F: FnOnce()
{
    조건부실행 { 조건, 실행함수: Some(실행함수) }
}

struct 조건부실행<F>
where
    F: FnOnce()
{
    조건: bool,
    실행함수: Option<F>
}

impl<F> 조건부실행<F>
where
    F: FnOnce()
{
    function 그렇지않으면<G>(mut self, 다른실행함수: G)
    where
        G: FnOnce()
    {
        if self.조건 {
            match self.실행함수.take() { case Some(함수) => 함수(), case None => {} }
        } else {
            다른실행함수()
        }
    }
    
    function 실행(mut self) {
        if self.조건 {
            match self.실행함수.take() { case Some(함수) => 함수(), case None => {} }
        }
    }
} test {
    // 파이프라인 테스트
    let 결과 = 파이프라인(10)
        .map(|x| x * 2)
        .map(|x| x + 5)
        .filter(|&x| x > 20)
        .map(|x| x.to_string())
        .finish()
    
    assert 결과 == Some("25".to_string())
    
    // 배열 처리 테스트
    let 배열처리기 = 배열처리::<i32>()
    let 숫자들 = vec![1, 2, 3, 4, 5]
    
    let 제곱들 = 배열처리기.map(숫자들.clone(), |x| x * x)
    assert 제곱들 == vec![1, 4, 9, 16, 25]
    
    let 짝수들 = 배열처리기.filter(숫자들.clone(), |&x| x % 2 == 0)
    assert 짝수들 == vec![2, 4]
    
    let 합계 = 배열처리기.fold(숫자들, 0, |acc, x| acc + x)
    assert 합계 == 15
    
    // 조건부 실행 테스트
    let mut 실행됨 = false
    
    만약(true, || {
        실행됨 = true
    }).실행()
    
    assert 실행됨 == true
}

// 사용 예시
let 처리결과 = 파이프라인(vec![1, 2, 3, 4, 5])
    .map(|배열| 배열처리::<i32>().map(배열, |x| x * 2))
    .map(|배열| 배열처리::<i32>().filter(배열, |&x| x > 5))
    .finish()

print!("처리된 배열: {:?}", 처리결과)

만약(처리결과.len() > 2, || {
    print!("배열이 충분히 큽니다!")
}).그렇지않으면(|| {
    print!("배열이 너무 작습니다.")
})

지연 평가와 스트림

// 지연 평가 시퀀스
struct 지연시퀀스<T, F>
where
    F: Fn() -> Option<T>
{
    생성함수: F
}

impl<T, F> 지연시퀀스<T, F>
where
    F: Fn() -> Option<T>
{
    function new(생성함수: F) -> Self {
        지연시퀀스 { 생성함수 }
    }
    
    function take(self, 개수: usize) -> Vec<T> {
        let mut 결과 = Vec::new()
        
        for _ in 0..개수 {
            match (self.생성함수)() { case Some(값) => 결과.push(값), case None => break }
        }
        
        결과
    }
}

// 무한 수열 생성기
function 무한수열생성(시작: int, 간격: int) -> impl Fn() -> Option<int> {
    let mut 현재 = 시작
    
    move || -> Option<int> {
        let= 현재
        현재 += 간격
        Some(값)
    }
}

// 피보나치 수열 생성기
function 피보나치생성기() -> impl Fn() -> Option<int> {
    let mut a = 0
    let mut b = 1
    
    move || -> Option<int> {
        let 현재 = a
        let 다음 = a + b
        a = b
        b = 다음
        Some(현재)
    }
}

// 조건부 무한 수열
function 조건부수열<F, P>(생성함수: F, 조건: P) -> impl Fn() -> Option<int>
where
    F: Fn() -> int,
    P: Fn(int) -> bool
{
    move || -> Option<int> {
        loop {
            let= 생성함수()
            if 조건(값) {
                return Some(값)
            }
        }
    }
}

// 스트림 처리기
struct 스트림<T> {
    데이터: Vec<T>
}

impl<T> 스트림<T>
where
    T: Clone
{
    function from(데이터: Vec<T>) -> Self {
        스트림 { 데이터 }
    }
    
    function map<U, F>(self, 함수: F) -> 스트림<U>
    where
        F: Fn(T) -> U
    {
        스트림 {
            데이터: self.데이터.into_iter().map(함수).collect()
        }
    }
    
    function filter<F>(self, 조건: F) -> 스트림<T>
    where
        F: Fn(&T) -> bool
    {
        스트림 {
            데이터: self.데이터.into_iter().filter(조건).collect()
        }
    }
    
    function take(self, 개수: usize) -> 스트림<T> {
        스트림 {
            데이터: self.데이터.into_iter().take(개수).collect()
        }
    }
    
    function collect(self) -> Vec<T> {
        self.데이터
    }
} test {
    // 무한 수열 테스트
    let 등차수열 = 무한수열생성(1, 2)  // 1, 3, 5, 7, ...
    let 첫다섯개 = (0..5).map(|_| 등차수열()).collect::<Vec<_>>()
    
    assert 첫다섯개 == vec![Some(1), Some(3), Some(5), Some(7), Some(9)]
    
    // 피보나치 테스트
    let 피보나치 = 피보나치생성기()
    let 피보나치첫여섯개: Vec<_> = (0..6).map(|_| 피보나치()).collect()
    
    assert 피보나치첫여섯개 == vec![Some(0), Some(1), Some(1), Some(2), Some(3), Some(5)]
    
    // 스트림 처리 테스트
    let 결과 = 스트림::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        .filter(|&x| x % 2 == 0)  // 짝수만
        .map(|x| x * x)           // 제곱
        .take(3)                  // 처음 3개
        .collect()
    
    assert 결과 == vec![4, 16, 36]  // 2², 4², 6²
}

// 사용 예시
let 홀수생성기 = 무한수열생성(1, 2)
print!("첫 10개 홀수: {:?}", (0..<10).map(|_| 홀수생성기()).collect::<Vec<_>>())

let 피보나치 = 피보나치생성기()
print!("피보나치 수열: {:?}", (0..8).map(|_| 피보나치()).collect::<Vec<_>>())

let 스트림결과 = 스트림::from((1..=20).collect())
    .filter(|&x| x % 3 == 0)    // 3의 배수
    .map(|x| format!("{}번", x))  // 문자열 변환
    .take(4)
    .collect()

print!("3의 배수들: {:?}", 스트림결과)

🎯 고급 클로저 기법

동적 함수 생성

// 런타임 함수 생성기
enum 연산타입 {
    더하기,
    빼기,
    곱하기,
    나누기
}

function 연산함수생성(연산: 연산타입, 피연산자: f64) -> Box<dyn Fn(f64) -> f64> {
    match 연산 {
        case 연산타입::더하기 => Box::new(move |x| x + 피연산자),
        case 연산타입::빼기 => Box::new(move |x| x - 피연산자),
        case 연산타입::곱하기 => Box::new(move |x| x * 피연산자),
        case 연산타입::나누기 => Box::new(move |x| {
            if 피연산자 != 0.0 { x / 피연산자 } else { f64::NAN }
        })
    }
}

// 조건부 함수 체인
struct 함수체인<T> {
    함수들: Vec<Box<dyn Fn(T) -> T>>
}

impl<T> 함수체인<T>
where
    T: 'static
{
    function new() -> Self {
        함수체인 { 함수들: Vec::new() }
    }
    
    function 추가<F>(mut self, 함수: F) -> Self
    where
        F: Fn(T) -> T + 'static
    {
        self.함수들.push(Box::new(함수))
        self
    }
    
    function 조건부추가<F>(mut self, 조건: bool, 함수: F) -> Self
    where
        F: Fn(T) -> T + 'static
    {
        if 조건 {
            self.함수들.push(Box::new(함수))
        }
        self
    }
    
    function 실행(self, 초기값: T) -> T {
        self.함수들.into_iter().fold(초기값, |값, 함수| 함수(값))
    }
}

// 설정 가능한 검증기 생성
struct 검증규칙<T> {
    이름: string,
    검증함수: Box<dyn Fn(&T) -> bool>
}

struct 검증기<T> {
    규칙들: Vec<검증규칙<T>>
}

impl<T> 검증기<T> {
    function new() -> Self {
        검증기 { 규칙들: Vec::new() }
    }
    
    function 규칙추가<F>(mut self, 이름: string, 검증함수: F) -> Self
    where
        F: Fn(&T) -> bool + 'static
    {
        self.규칙들.push(검증규칙 { 
            이름, 
            검증함수: Box::new(검증함수) 
        })
        self
    }
    
    function 검증(&self, 값: &T) -> Result<(), Vec<string>> {
        let mut 오류들 = Vec::new()
        
        for 규칙 in &self.규칙들 {
            if !(규칙.검증함수)(값) {
                오류들.push(규칙.이름.clone())
            }
        }
        
        if 오류들.is_empty() {
            Ok(())
        } else {
            Err(오류들)
        }
    }
} test {
    // 동적 함수 생성 테스트
    let 더하기10 = 연산함수생성(연산타입::더하기, 10.0)
    let 곱하기2 = 연산함수생성(연산타입::곱하기, 2.0)
    
    assert 더하기10(5.0) == 15.0
    assert 곱하기2(7.0) == 14.0
    
    // 함수 체인 테스트
    let 체인 = 함수체인::new()
        .추가(|x: i32| x + 1)
        .조건부추가(true, |x: i32| x * 2)
        .추가(|x: i32| x - 3)
    
    let 결과 = 체인.실행(5)  // ((5 + 1) * 2) - 3 = 9
    assert 결과 == 9
    
    // 검증기 테스트
    let 숫자검증기 = 검증기::new()
        .규칙추가("양수여야함".to_string(), |&x: &i32| x > 0)
        .규칙추가("100보다작아야함".to_string(), |&x: &i32| x < 100)
        .규칙추가("짝수여야함".to_string(), |&x: &i32| x % 2 == 0)
    
    assert 숫자검증기.검증(&50).is_ok()
    assert 숫자검증기.검증(&-5).is_err()
    assert 숫자검증기.검증(&51).is_err()  // 홀수이므로 실패
}

// 사용 예시
let 계산체인 = 함수체인::new()
    .추가(|x: f64| x * 1.5)        // 50% 증가
    .조건부추가(true, |x| x + 10.0) // 10 추가
    .추가(|x| x.round())            // 반올림

let 최종결과 = 계산체인.실행(7.3)
print!("계산 결과: {}", 최종결과)

let 문자열검증 = 검증기::new()
    .규칙추가("비어있지않음".to_string(), |s: &string| !s.is_empty())
    .규칙추가("최소길이".to_string(), |s: &string| s.len() >= 3)
    .규칙추가("특수문자포함".to_string(), |s: &string| s.contains('@'))

match 문자열검증.검증(&"test@example.com".to_string()) {
    case Ok(()) => print!("검증 성공!"),
    case Err(오류들) => print!("검증 실패: {:?}", 오류들)
}

플러그인 시스템

// 플러그인 인터페이스
trait 플러그인 {
    function 이름(&self) -> &str
    function 실행(&self, 입력: &str) -> string
}

// 클로저 기반 플러그인
struct 클로저플러그인<F>
where
    F: Fn(&str) -> string
{
    이름: string,
    실행함수: F
}

impl<F> 클로저플러그인<F>
where
    F: Fn(&str) -> string
{
    function new(이름: string, 실행함수: F) -> Self {
        클로저플러그인 { 이름, 실행함수 }
    }
}

impl<F> 플러그인 for 클로저플러그인<F>
where
    F: Fn(&str) -> string
{
    function 이름(&self) -> &str {
        &self.이름
    }
    
    function 실행(&self, 입력: &str) -> string {
        (self.실행함수)(입력)
    }
}

// 플러그인 매니저
struct 플러그인매니저 {
    플러그인들: Vec<Box<dyn 플러그인>>
}

impl 플러그인매니저 {
    function new() -> Self {
        플러그인매니저 { 플러그인들: Vec::new() }
    }
    
    function 플러그인등록<P>(mut self, 플러그인: P) -> Self
    where
        P: 플러그인 + 'static
    {
        self.플러그인들.push(Box::new(플러그인))
        self
    }
    
    function 모든플러그인실행(&self, 입력: &str) -> Vec<(string, string)> {
        self.플러그인들
            .iter()
            .map(|플러그인| (플러그인.이름().to_string(), 플러그인.실행(입력)))
            .collect()
    }
    
    function 플러그인찾기(&self, 이름: &str) -> Option<&dyn 플러그인> {
        self.플러그인들
            .iter()
            .find(|플러그인| 플러그인.이름() == 이름)
            .map(|플러그인| 플러그인.as_ref())
    }
}

// 미들웨어 시스템
type 미들웨어<T> = Box<dyn Fn(T, Box<dyn Fn(T) -> T>) -> T>

struct 미들웨어체인<T> {
    미들웨어들: Vec<미들웨어<T>>
}

impl<T> 미들웨어체인<T>
where
    T: 'static
{
    function new() -> Self {
        미들웨어체인 { 미들웨어들: Vec::new() }
    }
    
    function 미들웨어추가<F>(mut self, 미들웨어: F) -> Self
    where
        F: Fn(T, Box<dyn Fn(T) -> T>) -> T + 'static
    {
        self.미들웨어들.push(Box::new(미들웨어))
        self
    }
    
    function 실행<F>(self, 입력: T, 최종핸들러: F) -> T
    where
        F: Fn(T) -> T + 'static
    {
        let mut 체인 = Box::new(최종핸들러) as Box<dyn Fn(T) -> T>
        
        for 미들웨어 in self.미들웨어들.into_iter().rev() {
            let 이전체인 = 체인
            체인 = Box::new(move |입력| 미들웨어(입력, 이전체인))
        }
        
        체인(입력)
    }
} test {
    // 플러그인 시스템 테스트
    let 매니저 = 플러그인매니저::new()
        .플러그인등록(클로저플러그인::new(
            "대문자변환".to_string(),
            |텍스트| 텍스트.to_uppercase()
        ))
        .플러그인등록(클로저플러그인::new(
            "길이계산".to_string(),
            |텍스트| format!("길이: {}", 텍스트.len())
        ))
        .플러그인등록(클로저플러그인::new(
            "역순변환".to_string(),
            |텍스트| 텍스트.chars().rev().collect()
        ))
    
    let 결과들 = 매니저.모든플러그인실행("hello world")
    assert 결과들.len() == 3
    
    match 매니저.플러그인찾기("대문자변환") { case Some(대문자플러그인) => assert 대문자플러그인.실행("test") == "TEST", case None => {} }
    
    // 미들웨어 테스트
    let 체인 = 미들웨어체인::new()
        .미들웨어추가(|: i32, 다음| {
            print!("미들웨어 1: 입력 {}", 값)
            let 결과 = 다음(값 + 1)
            print!("미들웨어 1: 출력 {}", 결과)
            결과
        })
        .미들웨어추가(|값, 다음| {
            print!("미들웨어 2: 입력 {}", 값)
            let 결과 = 다음(값 * 2)
            print!("미들웨어 2: 출력 {}", 결과)
            결과
        })
    
    let 최종결과 = 체인.실행(5, || {
        print!("최종 핸들러: {}", 값)
+ 10
    })
    
    assert 최종결과 == 22  // ((5 + 1) * 2) + 10
}

// 사용 예시
let 텍스트처리매니저 = 플러그인매니저::new()
    .플러그인등록(클로저플러그인::new(
        "HTML이스케이프".to_string(),
        |텍스트| 텍스트.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
    ))
    .플러그인등록(클로저플러그인::new(
        "마크다운강조".to_string(),
        |텍스트| format!("**{}**", 텍스트)
    ))

let 처리결과들 = 텍스트처리매니저.모든플러그인실행("Hello <world> & friends!")

for (플러그인명, 결과) in 처리결과들 {
    print!("{}: {}", 플러그인명, 결과)
}

🔍 클로저 성능 최적화

메모리 효율적인 클로저

// 참조 기반 클로저 (메모리 효율적)
function 참조기반클로저(데이터: &[i32]) -> impl Fn(usize) -> Option<i32> + '_ {
    move |인덱스| 데이터.get(인덱스).copied()
}

// 스마트 포인터 활용
use std::rc::Rc
use std::sync::Arc

function 공유클로저생성(공유데이터: Rc<Vec<i32>>) -> impl Fn(usize) -> Option<i32> {
    move |인덱스| 공유데이터.get(인덱스).copied()
}

function 스레드안전클로저생성(공유데이터: Arc<Vec<i32>>) -> impl Fn(usize) -> Option<i32> + Send + Sync {
    move |인덱스| 공유데이터.get(인덱스).copied()
}

// 지연 초기화 클로저
struct 지연초기화<T, F>
where
    F: FnOnce() -> T
{
    초기화함수: Option<F>,
: Option<T>
}

impl<T, F> 지연초기화<T, F>
where
    F: FnOnce() -> T
{
    function new(초기화함수: F) -> Self {
        지연초기화 {
            초기화함수: Some(초기화함수),
: None
        }
    }
    
    function get(&mut self) -> &T {
        if self..is_none() {
            let 초기화함수 = self.초기화함수.take().unwrap()
            self.= Some(초기화함수())
        }
        self..as_ref().unwrap()
    }
}

// 클로저 풀링
struct 클로저풀<F, T, U>
where
    F: Fn(T) -> U
{
    함수들: Vec<F>,
    현재인덱스: usize
}

impl<F, T, U> 클로저풀<F, T, U>
where
    F: Fn(T) -> U + Clone
{
    function new(함수: F, 크기: usize) -> Self {
        클로저풀 {
            함수들: vec![함수; 크기],
            현재인덱스: 0
        }
    }
    
    function 실행(&mut self, 입력: T) -> U {
        let 함수 = &self.함수들[self.현재인덱스]
        self.현재인덱스 = (self.현재인덱스 + 1) % self.함수들.len()
        함수(입력)
    }
} test {
    // 공유 클로저 테스트
    let 데이터 = Rc::new(vec![1, 2, 3, 4, 5])
    let 클로저1 = 공유클로저생성(데이터.clone())
    let 클로저2 = 공유클로저생성(데이터.clone())
    
    assert 클로저1(0) == Some(1)
    assert 클로저2(4) == Some(5)
    
    // 지연 초기화 테스트
    let mut 지연값 = 지연초기화::new(|| {
        print!("비용이 큰 계산 실행됨")
        42
    })
    
    assert *지연값.get() == 42
    assert *지연값.get() == 42  // 두 번째 호출에서는 계산 안 함
    
    // 클로저 풀 테스트
    let mut= 클로저풀::new(|x: i32| x * 2, 3)
    
    assert 풀.실행(5) == 10
    assert 풀.실행(3) == 6
}

// 성능 벤치마킹을 위한 클로저
function 벤치마크<F, T>(이름: &str, 함수: F, 입력: T) -> T
where
    F: FnOnce(T) -> T
{
    use std::time::Instant
    
    let 시작시간 = Instant::now()
    let 결과 = 함수(입력)
    let 경과시간 = 시작시간.elapsed()
    
    print!("{} 실행 시간: {:?}", 이름, 경과시간)
    결과
}

// 캐시가 있는 클로저
use std::collections::HashMap

struct 캐시클로저<F, K, V>
where
    F: Fn(K) -> V,
    K: Clone + std::hash::Hash + Eq,
    V: Clone
{
    함수: F,
    캐시: HashMap<K, V>
}

impl<F, K, V> 캐시클로저<F, K, V>
where
    F: Fn(K) -> V,
    K: Clone + std::hash::Hash + Eq,
    V: Clone
{
    function new(함수: F) -> Self {
        캐시클로저 {
            함수,
            캐시: HashMap::new()
        }
    }
    
    function 실행(&mut self, 키: K) -> V {
        match self.캐시.get(&키) { case Some(값) => return.clone(), case None => {} }
        
        let 결과 = (self.함수)(키.clone())
        self.캐시.insert(키, 결과.clone())
        결과
    }
    
    function 캐시크기(&self) -> usize {
        self.캐시.len()
    }
    
    function 캐시지우기(&mut self) {
        self.캐시.clear()
    }
}

// 사용 예시
let 공유벡터 = Arc::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
let 접근클로저 = 스레드안전클로저생성(공유벡터)

print!("값 접근: {:?}", 접근클로저(3))

let mut 피보나치캐시 = 캐시클로저::new(|n: i32| -> i64 {
    if n <= 1 { n as i64 } else { 
        // 실제로는 재귀적으로 캐시를 사용해야 하지만, 
        // 여기서는 단순화된 버전
        (n as i64).pow(2)  // 예시를 위한 간단한 계산
    }
})

벤치마크("피보나치 계산", |_| {
    for i in 1..=20 {
        피보나치캐시.실행(i)
    }
}, ())

print!("캐시 크기: {}", 피보나치캐시.캐시크기())

🎯 클로저 마스터하기

클로저는 토파즈의 가장 강력한 기능 중 하나입니다:

✅ 클로저 사용 시기:

  • 콜백 함수와 이벤트 처리
  • 고차 함수 구현
  • 상태 캡슐화가 필요한 경우
  • 지연 실행부분 적용

⚠️ 주의사항:

  • 메모리 누수 방지 (순환 참조 주의)
  • 성능 고려 (불필요한 클로저 생성 피하기)
  • 라이프타임 관리 (적절한 캡처 방식 선택)
  • 디버깅 복잡성 인식

🚀 토파즈 클로저의 장점:

  • 타입 안전성 보장
  • 자동 메모리 관리
  • 다양한 캡처 방식 지원
  • 함수형 프로그래밍 패러다임 완벽 지원

클로저와 함께 더욱 표현력 있는 코드를 작성하세요! ✨🚀