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 yield | Meaning |
|---|---|
fn | declaration modifier |
{ | block marker (expression position) |
|| or | | closure modifier |
| expression-start token | yield 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.