클로저와 반복자: LINQ를 넘어서는 성능과 유연성
학습 목표: Rust의 **클로저(Closures)**가 소유권을 어떻게 다루는지 배우고, C#의 LINQ에 대응하는 **반복자(Iterators)**를 마스터합니다. '제로 비용 추상화'를 통해 함수형 프로그래밍의 편리함과 네이티브 루프의 성능을 동시에 잡는 비결을 익힙니다.
1. 클로저: 소유권을 인식하는 익명 함수
C#의 람다는 외부 변수를 항상 참조로 캡처하지만, Rust는 상황에 따라 빌림이나 **이동(Move)**을 선택할 수 있습니다.
Fn: 데이터를 불변으로 빌려옵니다. 여러 번 호출 가능합니다.FnMut: 데이터를 가변으로 빌려와 수정합니다. 여러 번 호출 가능합니다.FnOnce: 데이터의 소유권을 가져갑니다. 딱 한 번만 호출할 수 있습니다.
#![allow(unused)] fn main() { // [move 클로저 예시] let data = vec![1, 2, 3]; let handle = thread::spawn(move || { // data의 소유권이 스레드 내부로 이동됨 println!("{:?}", data); }); }
2. 반복자(Iterator): Rust의 LINQ
Rust의 반복자는 C#의 LINQ와 매우 흡사한 선언적 문법을 제공합니다. 하지만 런타임 오버헤드가 있는 LINQ와 달리, Rust 반복자는 최적화된 for 루프와 동일한 성능을 냅니다.
| 비교 항목 | C# (LINQ) | Rust (Iterator) |
|---|---|---|
| 평가 방식 | 지연 평가 (Lazy) | 지연 평가 (Lazy) |
| 성능 오버헤드 | 가상 호출, 할당 발생 가능 | 제로 비용 (인라인화됨) |
| 추상화 단위 | IEnumerable<T> | Iterator 트레이트 |
| 최종 수집 | .ToList(), .ToArray() | .collect::<Vec<_>>() |
3. 주요 연산자 매핑
| LINQ | Rust Iterator | 비고 |
|---|---|---|
.Select(x => ...) | `.map( | x |
.Where(x => ...) | `.filter( | x |
.SelectMany(...) | .flat_map(...) | 중첩된 구조를 평탄화 |
.Any(...) / .All(...) | .any(...) / .all(...) | 논리 조건 검사 |
.Aggregate(...) | .fold(...) | 값을 하나로 축약 |
💡 실무 팁: itertools 크레이트
C#의 GroupBy, Distinct, Chunk 같은 더 강력한 기능이 필요하다면 itertools 크레이트를 사용하세요. 표준 라이브러리를 보완하는 다양한 연산자를 제공하여 LINQ와 거의 동등한(혹은 그 이상의) 표현력을 갖출 수 있습니다.
#![allow(unused)] fn main() { use itertools::Itertools; // [중복 제거 예시] let unique_items = items.into_iter().unique().collect_vec(); }