Yes, in the same way that |...| ...
closures are a mechanism to create instances of an ad-hoc type that implements the Fn...
interface,
Example
let one: i32 = 1;
let increment = move |y: i32| y + one;
is sugar for:
let one: i32 = 1;
let increment = {
struct Closure {
// captures
one: i32,
}
impl Fn... for Closure {
...
fn (self: ..., y: i32) -> i32
{
y + self.one
}
}
Closure { one }
};
|...| { ... yield ... }
generators will be a mechanism to define an ad-hoc type that implements the Generator
interface, which is very similar to the Iterator
one (it just has bit more versatility). But we can actually discard that versatily (generator's .next()
function, called .resume()
, can receive some state, and when the generator ends, it can also give some state back, where an iterator cannot. But if we discard both these states, there is a trivial conversion from a (pinned) generator to an iterator:
#![feature(generators, generator_trait)]
mod helper {
use ::std::{
pin::Pin,
ops::{Generator, GeneratorState},
};
pub
fn generator_to_iterator<'gen, Item> (
mut gen: impl Generator<Yield = Item, Return = ()> + Unpin + 'gen,
) -> impl Iterator<Item = Item> + 'gen
{
::std::iter::from_fn(move || match Pin::new(&mut gen).resume(()) {
| GeneratorState::Yielded(item) => Some(item),
| GeneratorState::Complete(()) => None,
})
}
}
This way, one can now write things like:
fn prime_divisors (mut n: u64) -> impl Iterator<Item = u64>
{
let mut p = 2; // extra initial state
helper::generator_to_iterator(move /* n, p */ || {
while n > 1 {
while n % p != 0 {
p += 1; // quite naive, for the sake of the example
}
yield p;
n /= p;
}
})
}
And what the Rust compiler has done is convert the above into something with the semantics of:
fn prime_divisors (n: u64) -> impl Iterator<Item = u64>
{
let p = 2; // extra initial state
return MyIterator { n, p };
// where
struct MyIterator { n: u64, p: u64 }
impl Iterator for MyIterator {
type Item = u64;
fn next (self: &'_ mut Self)
-> Option<u64>
{
if self.n > 1 {
while self.n % self.p != 0 {
self.p += 1;
}
self.n /= self.p; // prepare state for the next `.next()` call.
return Some(self.p);
}
None // done
}
}
}
And, to be honest, I have cheated a bit with this example, technically the true translation is a full-fledged state machine,
- (and one that can even feature self-references, like
async
-generated Future
s do, which then requires pin_mut!
or Box::pin
in order to be pollable).
See, for instance,
let x: i32 = 42;
generator_to_iterator(move /* x */ || {
// <- initial state, captures x,
yield 2;
// <- state after the first yield, still captures x
let y: i32 = get_y();
yield 3;
// <- state after the second yield, captures `x` and `y`
yield x;
// <- state after the third yield, capturs `y`, but no longer needs to capture `x`
yield y;
// <- final state, does not need to capture anything (since this `return`s just a `()`)
would be converted into;
enum InternalState {
Initial { x: i32 },
AfterFirstYield { x: i32 },
AfterSecondYield { x: i32, y: i32 },
AfterThirdYield { y: i32 },
Final { /* nothing */ },
}
impl Generator for InternalState {
type Yield = i32;
type Return = ();
fn resume (self: Pin<&'_ mut Self>, _: ())
-> GeneratorState<i32, ()>
{
use GeneratorState::{Yielded, Completed};
use InternalState::*;
match *self {
| Initial { x } => {
*self = AfterFirstYield { x }; // prepare for next
Yielded(2)
},
| AfterFirstYield { x } => {
let y = get_y();
*self = AfterSecondYield { x, y }; // prepare...
Yielded(3)
},
| AfterSecondYield { x, y } => {
*self = AfterThirdYield { y };
Yielded(x)
},
| AfterThirdYield { y } => {
*self = Final { /* nothing */ };
Yielded(y)
},
| Final { .. } => {
Completed(())
},
}
}
}