2-3. iterator closure
· 6분 읽기
Day 18-20: Iterator & Closure
참고: The Rust Book Chapter 13 (Iterators and Closures)
1. 이터레이터 3종류 — 소유권 기반
let nums = vec![1, 2, 3];
// iter() → &T (읽기 전용, 원본 유지)
for n in nums.iter() { } // n: &i32
println!("{:?}", nums); // ✅ nums 살아있음
// iter_mut() → &mut T (수정 가능, 원본 유지)
let mut nums = vec![1, 2, 3];
for n in nums.iter_mut() { } // n: &mut i32
println!("{:?}", nums); // ✅ nums 살아있음
// into_iter() → T (소유권 이동, 원본 소비)
for n in nums.into_iter() { } // n: i32
// println!("{:?}", nums); // ❌ nums 소비됨선택 기준
| 상황 | 메서드 | 결과 타입 | 원본 |
|---|---|---|---|
| 읽기만 | iter() | &T | 유지 |
| 수정 필요 | iter_mut() | &mut T | 유지 |
| 소유권 이동 | into_iter() | T | 소비 |
Phase 1의 최소 권한 원칙:
&<&mut< 소유권 이동
2. 클로저 (Closure)
기본 문법
// Python: lambda x: x * 2
// Rust:
let double = |x| x * 2;
let double = |x: i32| -> i32 { x * 2 }; // 타입 명시 (선택)클로저 vs 함수: 외부 변수 캡처
let multiplier = 3;
// ✅ 클로저: 외부 변수 캡처 가능
let multiply = |x| x * multiplier;
println!("{}", multiply(5)); // 15
// ❌ fn: 외부 변수 접근 불가
fn multiply_fn(x: i32) -> i32 {
x * multiplier // 컴파일 에러!
}캡처 방식 — 최소 권한 자동 적용
// 1. &로 캡처 (읽기만 할 때)
let name = String::from("Rust");
let greet = || println!("{}", name);
greet();
println!("{}", name); // ✅ 살아있음
// 2. &mut로 캡처 (수정할 때)
let mut count = 0;
let mut inc = || count += 1;
inc();
println!("{}", count); // ✅ 살아있음 (값: 1)
// 3. move로 캡처 (소유권 이동, 명시적)
let name = String::from("Rust");
let greet = move || println!("{}", name);
greet();
// println!("{}", name); // ❌ 소유권 이동됨3. 이터레이터 체이닝
핵심 패턴: filter → map → collect
let nums = vec![1, 2, 3, 4, 5];
let result: Vec<i32> = nums.iter()
.filter(|x| **x >= 3) // 조건 필터링
.map(|x| x * 10) // 변환
.collect(); // Vec으로 수집
// [30, 40, 50]filter에서 이중 참조 주의
nums.iter() // &i32를 줌
.filter(|x| ...) // x는 &&i32 (참조의 참조!)
// 해결 방법
.filter(|x| **x >= 3) // 방법 1: 역참조
.filter(|&&x| x >= 3) // 방법 2: 패턴으로 벗기기map에서는 산술 연산 시 자동 역참조가 되어
*없이도 동작
Python과 비교
# Python
result = [x * 10 for x in nums if x >= 3]
# Python (map/filter 버전)
result = list(map(lambda x: x * 10, filter(lambda x: x >= 3, nums)))// Rust
let result: Vec<i32> = nums.iter()
.filter(|x| **x >= 3)
.map(|x| x * 10)
.collect();4. 주요 이터레이터 메서드
집계
let nums = vec![1, 2, 3, 4, 5];
nums.iter().sum::<i32>() // 15 (합계, 타입 명시 필요)
nums.iter().count() // 5 (개수)검색
nums.iter().any(|x| *x > 3) // true (하나라도 만족?)
nums.iter().all(|x| *x > 0) // true (전부 만족?)
nums.iter().find(|x| **x > 3) // Some(&4) (첫 번째 매칭)
nums.iter().position(|x| *x > 3) // Some(3) (인덱스)find, position은 값이 없을 수 있으므로
Option반환
enumerate — 인덱스 포함
let names = vec!["Alice", "Bob", "Carol"];
for (i, name) in names.iter().enumerate() {
println!("{}: {}", i, name);
}
// 0: Alice
// 1: Bob
// 2: Carol# Python 동일
for i, name in enumerate(names):
print(f"{i}: {name}")zip — 두 컬렉션 짝짓기
let names = vec!["Alice", "Bob"];
let scores = vec![85, 92];
let paired: Vec<_> = names.iter().zip(scores.iter()).collect();
// [("Alice", 85), ("Bob", 92)]flat_map — 중첩 펼치기
let sentences = vec!["hello world", "rust is fun"];
// map → 중첩
let nested: Vec<Vec<&str>> = sentences.iter()
.map(|s| s.split_whitespace().collect())
.collect();
// [["hello", "world"], ["rust", "is", "fun"]]
// flat_map → 평탄화
let flat: Vec<&str> = sentences.iter()
.flat_map(|s| s.split_whitespace())
.collect();
// ["hello", "world", "rust", "is", "fun"]# Python 동일
flat = [word for s in sentences for word in s.split()]format! — 문자열 포맷팅
let name = "Rust";
let version = 2026;
let msg = format!("{}: ${}", name, version); // "Rust: $2026"# Python 동일
msg = f"{name}: ${version}"5. 클로저를 함수 파라미터로 받기
fn vs Fn
// fn 포인터: 외부 변수 캡처 불가
fn apply(value: i32, func: fn(i32) -> i32) -> i32 {
func(value)
}
// Fn 트레이트: 캡처하는 클로저도 받을 수 있음
fn apply(value: i32, func: impl Fn(i32) -> i32) -> i32 {
func(value)
}
fn main() {
let multiplier = 3;
let result = apply(5, |x| x * multiplier); // Fn이면 ✅
println!("{}", result); // 15
}Fn 트레이트 3종류
| 트레이트 | 캡처 방식 | 호출 | 의미 |
|---|---|---|---|
Fn | &로 캡처 | 여러 번 | 읽기만 |
FnMut | &mut로 캡처 | 여러 번 | 수정 가능 |
FnOnce | move로 캡처 | 한 번만 | 소유권 이동 |
실무 기본:
Fn으로 시작 → 필요하면FnMut→FnOnce
6. ⚠️ Python 개발자 주의사항
| Python | Rust | 차이 |
|---|---|---|
lambda x: x * 2 | |x| x * 2 | 문법만 다름 |
for x in list: 후 list 사용 가능 | into_iter() 쓰면 원본 소비 | 소유권 주의 |
list(map(...)) | .map(...).collect() | collect()로 수집 필수 |
enumerate(list) | list.iter().enumerate() | 메서드 체이닝 |
| 이중 for 리스트 컴프리헨션 | .flat_map() | 평탄화 |
sum(list) | .iter().sum::<i32>() | 타입 명시 필요 |
7. 핵심 원칙 정리
- iter 선택: 읽기 →
iter(), 수정 →iter_mut(), 소비 →into_iter() - 클로저 캡처: Rust가 최소 권한으로 자동 결정 (
&<&mut< move) - filter 이중 참조:
iter().filter()에서**x또는|&&x|패턴 필요 - 체이닝 패턴:
.filter().map().collect()가 가장 흔한 조합 - 함수 파라미터: 클로저 받으려면
impl Fn사용 (fn포인터 아님) - collect 필수: 이터레이터는 lazy,
collect()로 실체화