Rust 반복자
반복자(iterator)는 값을 하나씩 차례대로 만들어 내는 것을 말해요. 사실 for 반복문 페이지부터 이미 쓰고 있었어요. 이름을 몰랐을 뿐이에요. 컬렉션을 도는 for 반복문은 모두 뒤에서 반복자가 움직이고 있거든요.
반복자 얻기
섹션 제목: “반복자 얻기”fn main() { let numbers = vec![1, 2, 3]; let mut iter = numbers.iter();
println!("{:?}", iter.next()); // Some(1) println!("{:?}", iter.next()); // Some(2) println!("{:?}", iter.next()); // Some(3) println!("{:?}", iter.next()); // None}실행 결과:
Some(1)Some(2)Some(3)None.next()는 Option을 돌려줘요. 아이템이 있으면 Some(값), 다 떨어지면 None이에요. for 반복문도 속을 들여다보면 None이 나올 때까지 .next()를 계속 호출하는 것뿐이에요.
map: 모든 아이템 바꾸기
섹션 제목: “map: 모든 아이템 바꾸기”.map()은 클로저를 받아서 모든 아이템에 적용하고, 새 반복자를 만들어요.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let doubled: Vec<i32> = numbers.iter().map(|n| n * 2).collect();
println!("{:?}", doubled);}실행 결과:
[2, 4, 6, 8, 10].collect()는 반복자를 다시 실제 컬렉션(여기서는 Vec<i32>)으로 바꿔 줘요. 이게 없으면 아직 아무것도 계산하지 않은 반복자만 덩그러니 남아 있게 돼요.
filter: 일부 아이템만 남기기
섹션 제목: “filter: 일부 아이템만 남기기”fn main() { let numbers = vec![1, 2, 3, 4, 5, 6]; let evens: Vec<&i32> = numbers.iter().filter(|n| **n % 2 == 0).collect();
println!("{:?}", evens);}실행 결과:
[2, 4, 6]**n처럼 별표가 두 개라 낯설어 보일 거예요. .filter()는 클로저에 참조자의 참조자(&&i32)를 넘겨주기 때문에, 일반 숫자와 비교하려면 역참조를 두 번 해야 해요. 자주 마주치는 작은 특징이니, 지금은 자세한 이유까지 몰라도 괜찮아요.
map과 filter 이어 붙이기
섹션 제목: “map과 filter 이어 붙이기”반복자 메서드를 이어 붙이면(체이닝) 진가가 드러나요. 여러 단계를 읽기 쉬운 파이프라인 하나로 묶을 수 있거든요.
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers .iter() .filter(|n| **n % 2 == 0) // 짝수만 남기기 .map(|n| n * 10) // 각각 10 곱하기 .collect();
println!("{:?}", result);}실행 결과:
[20, 40, 60]같은 로직을 for 반복문과 빈 Vec에 push하는 방식으로 써 봐도 돼요. 틀린 방법은 아니에요. 하지만 체이닝한 쪽이 “거르고, 곱하고, 모은다”라는, 머릿속으로 생각한 순서 그대로 읽혀요.
그 밖에 자주 쓰는 메서드
섹션 제목: “그 밖에 자주 쓰는 메서드”fn main() { let numbers = vec![1, 2, 3, 4, 5];
let total: i32 = numbers.iter().sum(); let count = numbers.iter().count(); let has_even = numbers.iter().any(|n| n % 2 == 0);
println!("합계: {total}, 개수: {count}, 짝수 있음: {has_even}");}실행 결과:
합계: 15, 개수: 5, 짝수 있음: true.sum()은 모두 더하고, .count()는 아이템이 몇 개인지 알려 주고, .any()는 “조건에 맞는 아이템이 하나라도 있나요?“에 답해요. 이건 모든 반복자가 가진 메서드 중 일부일 뿐이에요. enumerate, zip, take, rev처럼 훨씬 많아요. 사용자 정의 반복자 페이지에서는 반복자를 직접 만들어 보고, 그 반복자도 이 메서드들을 공짜로 얻는다는 걸 확인할 거예요.
연습 문제
섹션 제목: “연습 문제”1. 홀수 제곱의 합
섹션 제목: “1. 홀수 제곱의 합”1부터 10까지의 숫자 중 홀수만 남기고, 각각 제곱한 다음, 모두 더해 보세요. mut 변수 없이 반복자 체인 하나로 해 보세요. 힌트: .filter(), .map(), .sum()을 이어 붙이고, 결과에는 let total: i32처럼 타입을 적어 주세요.
정답 보기
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let total: i32 = numbers .iter() .filter(|n| **n % 2 == 1) // 1, 3, 5, 7, 9만 남기기 .map(|n| n * n) // 1, 9, 25, 49, 81 .sum();
println!("홀수 제곱의 합: {total}");}실행 결과:
홀수 제곱의 합: 165total에 : i32가 필요한 이유는 .sum()이 여러 숫자 타입으로 더할 수 있어서, 어떤 타입을 원하는지 Rust가 추측할 수 없기 때문이에요.
2. 긴 단어를 대문자로
섹션 제목: “2. 긴 단어를 대문자로”vec!["the", "quick", "brown", "fox", "jumps"]에서 시작해요. 3글자보다 긴 단어만 대문자로 바꿔 새 Vec<String>을 만들고, {:?}로 출력해 보세요. 힌트: .filter(|w| w.len() > 3)로 긴 단어만 남기고, .to_uppercase()로 &str을 대문자 String으로 바꿀 수 있어요.
정답 보기
fn main() { let words = vec!["the", "quick", "brown", "fox", "jumps"];
let loud: Vec<String> = words .iter() .filter(|w| w.len() > 3) .map(|w| w.to_uppercase()) .collect();
println!("{:?}", loud);}실행 결과:
["QUICK", "BROWN", "JUMPS"]words 자체는 바뀌지 않아요. .iter()는 words를 빌리기만(대여) 하니까, 체인이 끝난 뒤에도 words를 계속 쓸 수 있어요.
이 문제의 단어는 영어 그대로 두었어요. .len()은 글자 수가 아니라 바이트 수를 세는데, 한글은 한 글자가 3바이트라서 결과가 달라지거든요.
- 반복자는
.next()로 값을 하나씩 만들어 내고,.next()는Option을 돌려줘요. for반복문도 속에서는 반복자로 동작해요..map()은 각 아이템을 바꾸고,.filter()는 조건에 맞는 것만 남기고,.collect()는 결과를 다시 실제 컬렉션으로 만들어요.- 반복자 메서드를 이어 붙이는 쪽이, 가변 누적 변수를 쓰는 수동 반복문보다 대개 더 알아보기 쉬워요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. 반복자에서 .next()는 무엇을 돌려줄까요?
2. .iter().map(...).collect() 같은 체인 끝의 .collect()는 무슨 일을 할까요?
3. .filter(|n| **n % 2 == 0)에서 n을 왜 두 번 역참조할까요?
4. for 반복문이 컬렉션을 돌 때, 뒤에서 무엇이 움직일까요?
5. .filter()를 거친 반복자에는 무엇이 남을까요?
점수: 0 / 5
더 알아보기
섹션 제목: “더 알아보기”- 반복자로 일련의 아이템들 처리하기 — The Rust Programming Language 한국어판:
next,map,filter,collect를 더 많은 예제와 함께 소개하는 공식 장이에요. - 성능 비교하기: 루프 vs. 반복자 — The Rust Programming Language 한국어판: 반복자 체인이 직접 쓴 반복문만큼 빠른 이유를 설명해요.
Iterator트레이트 — 표준 라이브러리 문서 (영어): 반복자 메서드 전체 목록이에요. 메서드마다 작은 예제가 있어서 훑어보기 좋아요.- Iterators — Rust by Example (영어): 피보나치 반복자를 만드는 짧은 예제를 실행해 볼 수 있어요.