rokdocs
v0.1

4.11. 제너레이터 (yield)

yield 키워드 (키워드) 는 값을 하나씩 만들어내는 함수, 즉 제너레이터를 정의하는 세 가지 형태에서 사용됩니다.

4.11.1. 세 가지 형태

제너레이터는 다음과 같은 문법으로 표현됩니다.

generator_fn    ::= "yield" fn_decl
generator_block ::= "yield" block
generator_closure ::= "yield" closure_expr
yield_stmt      ::= "yield" expr? ";"
yield fn name(args) -> R { ... yield expr; ... }     // 명명된 제너레이터 함수
yield { ... yield expr; ... }                         // 인라인 블록 (값)
yield || { ... yield expr; ... }                      // 클로저 수식어

세 형태 모두 결과는 라이브러리 타입 GenIter<T> (Iterator<Item = T> 구현) 의 값이며, throws (E) 면 TryGenIter<T, E> 입니다. 이 규칙은 4.1.11 에서 정의합니다.

4.11.2. yield statement: 값을 내보내기

yield 문은 값과 함께 또는 값 없이 사용할 수 있습니다.

yield expr;
yield;

yield expr; 형태는 expr 의 값을 현재 반복 단계의 산출값으로 내보내고, 함수나 블록의 평가를 일시 중단합니다. 다음 반복에서는 이어서 평가를 진행합니다. 한편 yield; 형태는 값 없이 (unit) 단계만 진행시킵니다.

4.11.3. 명명된 제너레이터 함수

명명된 제너레이터 함수는 다음과 같이 작성합니다.

yield fn count_up_to(n: i32) -> i32 {
    let i = 0;
    while i < n {
        yield i;
        i = i + 1;
    }
}

yield fn 은 제너레이터임을 표시하는 함수 선언이며, 본체 안에서는 yield expr; 으로 값을 내보냅니다. -> R 자리에는 yield 되는 값의 타입을 적으며, 호출 결과는 GenIter<R> 입니다. -> R 은 반드시 적어야 하며, 원소 타입을 본체에서 추론하지 않습니다. 호출 방식은 일반 함수와 동일하며, 결과는 반복자 객체가 됩니다.

let it = count_up_to(5i32);       // it: `GenIter<i32>`
for x in it { print(x); }         // 0 1 2 3 4

4.11.4. 인라인 yield 블록: 값

식 자리에서 즉시 반복자를 만들고 싶다면 인라인 yield 블록을 사용합니다.

let it = yield {
    yield 1i32;
    yield 2i32;
    yield 3i32;
};

이 형태는 식 자리에 등장하며, 즉시 GenIter<T> 를 만들어냅니다. 본체는 블록 표현식 의 블록입니다. 짧은 제너레이터를 익명 인라인으로 적기 위한 형태라고 할 수 있습니다.

4.11.5. yield 클로저

yield 클로저는 호출 가능한 제너레이터를 만들어냅니다.

let make_iter = yield || {
    yield 1i32;
    yield 2i32;
    yield 3i32;
};

let it = make_iter();             // 호출 시 새 Iterator 생성

yield || 는 호출 가능한 제너레이터 클로저를 만들며, 호출할 때마다 새 반복자가 생성됩니다. 외부 캡처는 캡처 의미론 의 규칙을 따릅니다.

4.11.6. 세 형태의 대응

제너레이터의 세 형태는 함수 선언, 클로저, 블록 표현식 의 세 형태 (명명, 블록, 클로저) 에 일대일로 대응됩니다.

일반 함수    fn f() { ... }       ↔  || { ... }       ↔  { ... } (블록)
yield 형     yield fn f() { ... } ↔  yield || { ... } ↔  yield { ... }

즉, yield 가 동일한 함수, 블록, 클로저 형태에 수식어로 붙어 그것을 제너레이터로 변환하는 셈입니다.

4.11.7. yield 토큰의 네 가지 등장 위치

yield 토큰은 네 가지 위치에서 등장할 수 있습니다.

yield fn name(...) { ... }        // declaration modifier (선언 수식어)
yield { ... }                      // block marker (블록 마커)
yield || { ... }                   // closure modifier (클로저 수식어)
yield expr;                         // statement (문장)

이 네 위치는 문맥으로 명확히 구분되며, 파서는 다음 토큰을 보고 어느 형태인지 결정합니다.

yield 다음 토큰의미
fndeclaration modifier
{block marker (식 자리)
|| 또는 |closure modifier
식 시작 토큰yield statement

4.11.8. Iterator 트레잇과의 결합

제너레이터 함수의 결과는 표준 라이브러리의 Iterator<Item = T> 트레잇을 구현하는 값이므로, 루프 의 for 루프에서 그대로 사용할 수 있습니다.

yield fn naturals() -> i32 {
    let i = 0;
    loop {
        yield i;
        i = i + 1;
    }
}

for n in naturals() {
    if n > 100 { break; }
    print(n);
}

Iterator 트레잇의 정확한 정의와 메서드 (map, filter, take, collect 등) 는 표준 라이브러리에서 정의됩니다.

4.11.9. 무한 제너레이터

위 예의 naturals() 는 끝없이 값을 내는 무한 제너레이터에 해당합니다. 이러한 제너레이터는 for 루프의 break 또는 라이브러리의 take(n) 메서드 등으로 종료시킬 수 있습니다.

4.11.10. 제너레이터의 종료

본체가 정상 종료하면 반복자 역시 종료되며, 다음 next() 호출에서는 none 이 반환됩니다.

yield fn finite() -> i32 {
    yield 1i32;
    yield 2i32;
    yield 3i32;
    // 함수 정상 종료 → 반복자 종료
}

또한 return; 으로 명시적으로 종료시킬 수도 있습니다.

yield fn early_stop(stop: i32) -> i32 {
    let i = 0;
    loop {
        if i >= stop { return; }
        yield i;
        i = i + 1;
    }
}

4.11.11. 제너레이터의 상태

제너레이터는 호출 사이에 내부 상태를 유지합니다. 본체의 지역 변수, 반복 위치, 코루틴 프레임이 다음 next() 호출까지 보존됩니다.

이 상태는 7. 런타임 모델 의 코루틴 인프라 위에서 작동하며, 컴파일러가 만든 상태 기계 (stackless) 로 동작합니다. 자세한 구현 메커니즘은 런타임 사양에서 정의됩니다.

4.11.12. yield 함수의 매개변수와 반환

yield fn 은 제너레이터이므로, -> T 는 본체에서 yield 되는 값의 타입을 가리키며, 호출 결과는 GenIter<T>입니다.

yield fn f() -> i32 { yield 1i32; }
// 호출 결과: `GenIter<i32>`

매개변수의 사용 방식은 일반 함수와 동일합니다.

4.11.13. 메서드로서의 제너레이터

메서드 의 impl 블록 안에서도 yield fn 을 사용해 제너레이터 메서드를 정의할 수 있습니다.

impl<T> List<T> {
    yield fn iter(self) -> T {
        for x in self.elements { yield x; }
    }
}

호출 방식과 동작은 일반 인스턴스 메서드와 동일합니다.

GitHub에서 이 페이지 고치기