# Generators

Generators are a convenient method for constructing functions which are capable of suspending themselves and their state.

The simplest use case for generators is to create a kind of iterator, whose state is stored in the generator function.

With this, we can create a fairly efficient generator to build fibonacci numbers.

``````fn fib() {
let a = 0;
let b = 1;

loop {
yield a;
let c = a + b;
a = b;
b = c;
}
}

let g = fib();

while let Some(n) = g.next() {
println!("{n}");

if n > 100 {
break;
}
}
``````
``````\$> cargo run -- run scripts/book/generators/fib_generator.rn
0
1
1
2
3
5
8
13
21
34
55
89
144
``````

## Advanced generators with `GeneratorState`

Generators internally are a bit more complex than that. The `next` function simply slates over some of that complexity to make simple things easier to do.

The first thing to know is that `yield` itself can actually produce a value, allowing the calling procedure to send values to the generator.

``````fn printer() {
let collected = [];

for _ in 0..2 {
let out = yield;
println!("{:?}", out);
collected.push(out);
}

assert_eq!(collected, ["John", (1, 2, 3)]);
}

let printer = printer();
printer.resume(());
printer.resume("John");
printer.resume((1, 2, 3));
``````
``````\$> cargo run -- run scripts/book/generators/send_values.rn
"John"
(1, 2, 3)
``````

But wait, what happened to the first value we sent, `1`?

Well, generators don't run immediately, they need to be "warmed up" by calling resume once. At that point it runs the block prior to the first yield, we can see this by instrumenting our code a little.

``````fn printer() {
loop {
println!("waiting for value...");
let out = yield;
println!("{out:?}");
}
}

let printer = printer();

println!("firing off the printer...");
printer.resume(());

printer.resume("John");
printer.resume((1, 2, 3));
``````
``````\$> cargo run -- run scripts/book/generators/bootup.rn
firing off the printer...
waiting for value...
"John"
waiting for value...
(1, 2, 3)
waiting for value...
``````

Ok, so we understand how to send values into a generator. But how do we receive them?

This adds a bit of complexity, since we need to pull out `GeneratorState`. This enum has two variants: `Yielded` and `Complete`, and represents all the possible states a generator can suspend itself into.

``````fn print_once() {
let out = yield 1;
println!("{:?}", out);
2
}

let printer = print_once();
dbg!(printer.resume(()));
dbg!(printer.resume("John"));
``````
``````\$> cargo run -- run scripts/book/generators/states.rn
Yielded(1)
"John"
Complete(2)
``````

After the first call to resume, we see that the generator produced `Yielded(1)`. This corresponds to the `yield 1` statement in the generator.

The second value we get is `Complete(2)`. This corresponds to the return value of the generator.

Trying to resume the generator after this will cause the virtual machine to error.

``````fn print_once() {
yield 1
}

let printer = print_once();
dbg!(printer);
dbg!(printer.resume(()));
dbg!(printer.resume("John"));
dbg!(printer);
dbg!(printer.resume(()));
``````
``````\$> cargo run -- run scripts/book/generators/error.rn
Generator { completed: false }
Yielded(1)
Complete("John")
Generator { completed: true }
error: virtual machine error
┌─ scripts/book/generators/error.rn:11:9
│
11 │     dbg!(printer.resume(()));
│          ^^^^^^^^^^^^^^^^^^ cannot resume a generator that has completed
``````