Iterator 트레이트의 연관 타입(associated type)

아래 코드의 핵심은 Iterator 트레이트의 연관 타입(associated type)트레이트 구현에 의한 타입 유추입니다. Item이 직접적으로 코드 안에서 보이지 않지만, 트레이트 시스템이 자동으로 Iterator trait의 type Item과 연결해 주고 있습니다.

1. 예제 코드

struct GivesOne;

impl Iterator for GivesOne {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        Some(1)
    }

}

fn main() {
    let five_ones = GivesOne.into_iter().take(5).collect::<Vec<i32>>();

    println!("{five_ones:?}");
}

2. 구조체 정의

struct GivesOne;
  • GivesOne은 단순한 빈 구조체입니다.

3.Iterator 트레이트 구현

impl Iterator for GivesOne {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        Some(1)
    }
}

여기서 중요한 부분이 바로 type Item = i32; 입니다.
Iterator 트레이트는 아래와 같이 정의되어 있습니다 (표준 라이브러리 축약본).

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

즉,

  • Item은 연관 타입으로, 이 이터레이터가 어떤 타입의 요소를 내보내는지를 지정합니다.
  • Self::Item이 Option<Self::Item> 안에서 쓰이죠.

    그런데 GivesOne에서는 type Item = i32;로 정했으니,
    → Self::Item이 i32가 됩니다.
    → 따라서 fn next의 실제 타입은 fn next(&mut self) -> Option<i32>가 되는 겁니다.

next()는다음 요소를 하나 반환하거나(None을 반환하면 종료) 하는 메서드입니다.
이터레이터는 내부 상태를 가지고 “이번에 무엇을 내보낼지”를 결정하죠.

GivesOne 구조체에는 아무 필드도 없습니다. 즉, 몇 번째 요소를 내보냈는지 추적하는 정보가 없습니다. 따라서, 이터레이터는 “항상 똑같은 값 1만 계속 내보내는” 무한 반복 이터레이터가 됩니다.

4. GivesOne.into_iter()의 동작

let five_ones = GivesOne.into_iter().take(5).collect::<Vec<i32>>();

여기서 헷갈릴 수 있는 부분은 into_iter()인데,
GivesOne이 스스로 Iterator를 구현하고 있기 때문에,
Rust는 GivesOne.into_iter()를 IntoIterator 트레이트를 통해 다음처럼 해석합니다.

impl<I: Iterator> IntoIterator for I {
    type Item = I::Item;
    type IntoIter = I;

    fn into_iter(self) -> I {
        self
    }
}

즉,

  • GivesOne이 이미 Iterator니까
    → IntoIterator도 자동으로 적용되고,
    → into_iter()는 그냥 자기 자신(GivesOne)을 반환합니다.

5. 체인 전체의 타입 흐름

  1. GivesOne.into_iter() → 타입: GivesOne
    위에서 알아본 바와 같이 next() 메소드는 영원히 Some(1)만 반환하고, None을 반환하지 않으므로 무한루프가 됩니다.
  2. .take(5) → 타입: Take<GivesOne>
    따라서.take(5)로 “앞의 5번만 가져오라”고 제한을 두는 것입니다.
  3. collect::<Vec<i32>>()
    collect()는 Item 타입을 기반으로 Vec<i32>를 만듭니다.

      6. 실제로 내부 타입이 어떻게 “보이지 않지만 작동하는가?”

      Rust는 트레이트 바인딩을 통해 자동으로 타입을 유추합니다.
      Item은 직접 변수로 존재하지 않고, 트레이트 정의 안에서 연관 타입으로만 쓰이죠.

      즉,
      GivesOne → Iterator → Item = i32
      이 연결이 트레이트 시스템 레벨에서 자동으로 이어지므로,
      어디에도 Item이라는 이름이 코드에 명시적으로 안 나와도,
      Rust는 Iterator<Item = i32>인 것을 정확히 압니다.

      7. 요약

      코드 부분역할
      type Item = i32;이터레이터가 내보내는 값의 타입을 지정
      fn next(&mut self)실제로 값을 하나씩 생성
      into_iter()Iterator를 자기 자신으로 반환
      take(5)최대 5번 next()호출
      collect::<Vec<i32>>()결과를 Vec<i32>로 수집

      결과적으로println!(“{five_ones:?}”)는 [1, 1, 1, 1, 1]을 출력하게 됩니다.

      Iterator 실행 결과