프로그래밍러스트 개발자 / 백엔드 개발자

러스트에서 반복자와 반복자 어댑터가 어떻게 작동하는지 설명해 주세요. 사용자 정의 반복자의 구현이 표준 어댑터를 사용하는 것과 어떤 차이가 있으며, 언제 사용자 정의 반복자를 구현해야 하나요?

Hintsage AI 어시스턴트로 면접 통과

답변

러스트의 모든 표준 컬렉션 라이브러리는 반복자 개념에 기반하고 있습니다. 반복자는 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<_>>(). 문제는 어노테이션을 추가함으로써 해결되었지만 원인을 찾는 데 몇 시간을 낭비했습니다.