러스트의 모든 표준 컬렉션 라이브러리는 반복자 개념에 기반하고 있습니다. 반복자는 Iterator 트레잇을 구현하는 객체로, 여기에는 next() 메서드가 정의되어 있습니다. 이 메서드는 데이터 시퀀스의 다음 요소를 반환합니다 (Option<T> 형식, 여기서 Some(T)는 다음 값, None은 시퀀스의 끝을 의미합니다).
표준 반복자 예시:
let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(x) = iter.next() { println!("{}", x); }
반복자 어댑터는 새로운 반복자를 반환하는 메서드입니다 (예: .map(), .filter(), .enumerate(), .take()) 이들은 전달된 함수에 따라 값을 '즉석에서' 처리합니다.
사용자 정의 반복자는 구조체를 생성하고 이 구조체에 대해 Iterator 트레잇을 구현하여 고유한 동작을 정의함으로써 구현됩니다:
struct Counter { count: u8 } impl Iterator for Counter { type Item = u8; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } }
표준 어댑터가 컬렉션을 처리하기에 충분할 때 사용하세요. 사용자 정의 반복자를 구현해야 하는 경우는 다음과 같습니다:
무한 반복자를 만들 수 있나요?
.collect()를 사용하여 컬렉션으로 수집하려고 하면 어떻게 되나요?
답변: 네, 러스트에서는 std::iter::repeat와 같은 무한 시퀀스를 반환하는 반복자가 존재합니다:
let mut endless = std::iter::repeat(1); endless.next(); // 무한히 Some(1)을 반환합니다
이런 반복자를 .collect()를 통해 컬렉션으로 수집하려고 하면 프로그램이 멈추거나 메모리 오버플로우로 중단됩니다. 왜냐하면 반복이 스스로 종료되지 않기 때문입니다!
이야기
REST API 프로젝트에서 1000 요소의 배열을 정렬한 후 .iter().filter(|x| *x > 500)를 사용했는데, .collect::<Vec<_>>() 대신 복잡한 어댑터 내에서 .fold(0, |acc, _| acc + 1)을 적용하여 반복이 제대로 완료될지에 대한 이해를 잃어버렸습니다. 결과: 내부 오류가 있는 유한한 반복자에 대한 필터링으로 인한 임의의 멈춤.
이야기
한 독점 엔진에서 고유한 id를 생성하기 위해 무작위 개발자가 한도가 도달했을 때 None을 반환하는 것을 잊고 사용자 정의 반복자를 구현했습니다. 그 결과 반복은 무한 루프에 빠졌고, 프로덕션 서버는 CPU를 다 소모하고 요청에 응답하지 않았습니다.
이야기
프론트엔드 부분 (WebAssembly 모듈)에서 중첩된 어댑터가 있는 반복자를 사용하여 .map().filter().skip()를 사용하고, 형식에 대한 결과를 얻기 위해 .collect()를 사용하였습니다. 어댑터 아래의 유형을 변경할 때 러스트는 유형 불일치에 대한 복잡한 컴파일 시간 오류를 보여주었습니다. 정확한 컬렉션 유형을 지정하는 것을 잊었기 때문입니다: .collect::<Vec<_>>(). 문제는 어노테이션을 추가함으로써 해결되었지만 원인을 찾는 데 몇 시간을 낭비했습니다.