rokdocs
v0.1

4.11. Generators (yield)

The yield keyword (keywords) is used in three forms that define generators, functions that produce values one at a time.

4.11.1. Three forms

Generators have this grammar:

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; ... }     // named generator function
yield { ... yield expr; ... }                         // inline block (value)
yield || { ... yield expr; ... }                      // closure modifier

In all three forms, the result is a value of the library type GenIter<T> (implementing Iterator<Item = T>), or TryGenIter<T, E> with throws (E). This rule is defined in 4.1.11.

4.11.2. The yield statement: emitting values

The yield statement may be used with or without a value.

yield expr;
yield;

yield expr; emits the value of expr as the output of the current iteration step and suspends evaluation of the function or block. Evaluation resumes on the next iteration. yield; advances a step without a value (unit).

4.11.3. Named generator functions

A named generator function is written as follows.

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

yield fn is a function declaration marked as a generator; the body emits values with yield expr;. The -> R position gives the type of the yielded values, and the call result is GenIter<R>. -> R must be written; the element type is not inferred from the body. It is called like an ordinary function, and the result is an iterator object.

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

4.11.4. Inline yield blocks: values

To create an iterator immediately in expression position, use an inline yield block.

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

This form appears in expression position and immediately produces a GenIter<T>. The body is a block from block expressions. It is the form for writing short generators anonymously inline.

4.11.5. yield closures

A yield closure produces a callable generator.

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

let it = make_iter();             // each call creates a new Iterator

yield || creates a callable generator closure; each call creates a new iterator. Outer captures follow the rules of Capture Semantics.

4.11.6. Correspondence of the three forms

The three generator forms correspond one-to-one to the three forms (named, block, closure) of function declarations, closures and block expressions.

plain        fn f() { ... }       ↔  || { ... }       ↔  { ... } (block)
yield form   yield fn f() { ... } ↔  yield || { ... } ↔  yield { ... }

That is, yield attaches as a modifier to the same function, block and closure forms and turns them into generators.

4.11.7. Four positions of the yield token

The yield token can appear in four positions.

yield fn name(...) { ... }        // declaration modifier
yield { ... }                      // block marker
yield || { ... }                   // closure modifier
yield expr;                         // statement

These four positions are clearly distinguished by context; the parser decides the form from the next token.

Token after yieldMeaning
fndeclaration modifier
{block marker (expression position)
|| or |closure modifier
expression-start tokenyield statement

4.11.8. Interaction with the Iterator trait

The result of a generator function is a value implementing the standard library's Iterator<Item = T> trait, so it can be used directly in a for loop from loops.

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

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

The exact definition and methods (map, filter, take, collect, etc.) of the Iterator trait are defined in the standard library.

4.11.9. Infinite generators

naturals() above is an infinite generator that produces values endlessly. Such generators can be terminated with break in a for loop, the library's take(n) method and the like.

4.11.10. Generator termination

When the body completes normally, the iterator also terminates, and the next next() call returns none.

yield fn finite() -> i32 {
    yield 1i32;
    yield 2i32;
    yield 3i32;
    // function completes normally → iterator terminates
}

It can also be terminated explicitly with return;.

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

4.11.11. Generator state

A generator keeps internal state between calls. The body's local variables, the iteration position and the coroutine frame are preserved until the next next() call.

This state works on the coroutine infrastructure of 7. Runtime Model and operates as a compiler-generated (stackless) state machine. The detailed implementation mechanism is defined in the runtime specification.

4.11.12. Parameters and return of yield functions

Since a yield fn is a generator, -> T denotes the type of the values yielded in the body, and the call result is GenIter<T>.

yield fn f() -> i32 { yield 1i32; }
// call result: `GenIter<i32>`

Parameters are used the same way as in ordinary functions.

4.11.13. Generators as methods

yield fn may also be used inside the impl block of a method to define generator methods.

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

Calls and behavior are the same as for ordinary instance methods.

Edit this page on GitHub