[][src]Module genawaiter::sync

This module implements a generator which can be shared between threads.

You can create a generator with Gen::new. Pass it a function that bootstraps the generator:

async fn producer(co: Co<i32>) { /* ... */ }

let mut generator = Gen::new(producer);

Storing a generator in a static

In Rust, the type of static variables must be nameable, but the type of an async fn is not nameable – async fns always return impl Future. So, in order to store a generator in a static, you'll need dyn Future, plus a layer of indirection. This crate provides the GenBoxed type alias with the Gen::new_boxed function to make this easier (and to smooth out a rough corner in the type inference).

Additionally, as usual when dealing with statics in Rust, you'll need some form of synchronization. Here is one possible pattern, using the once_cell crate.

This example is not tested
use genawaiter::sync::{Gen, GenBoxed};
use once_cell::sync::Lazy;
use std::sync::Mutex;

static INEFFICIENT_COUNTER: Lazy<Mutex<GenBoxed<i32>>> =
    Lazy::new(|| Mutex::new(Gen::new_boxed(|co| async move {
        let mut n = 0;
        loop {
            n += 1;
            co.yield_(n).await;
        }
    })));

Examples

Using Iterator

Generators implement Iterator, so you can use them in a for loop:

use genawaiter::sync::{Co, Gen};

async fn odd_numbers_less_than_ten(co: Co<i32>) {
    let mut n = 1;
    while n < 10 {
        co.yield_(n).await;
        n += 2;
    }
}

for num in Gen::new(odd_numbers_less_than_ten) {
    println!("{}", num);
}

Collecting into a Vec

let gen = Gen::new(odd_numbers_less_than_ten);
let xs: Vec<_> = gen.into_iter().collect();
assert_eq!(xs, [1, 3, 5, 7, 9]);

Using resume()

let mut gen = Gen::new(odd_numbers_less_than_ten);
assert_eq!(gen.resume(), GeneratorState::Yielded(1));
assert_eq!(gen.resume(), GeneratorState::Yielded(3));
assert_eq!(gen.resume(), GeneratorState::Yielded(5));
assert_eq!(gen.resume(), GeneratorState::Yielded(7));
assert_eq!(gen.resume(), GeneratorState::Yielded(9));
assert_eq!(gen.resume(), GeneratorState::Complete(()));

Using an async closure (nightly Rust only)

This example is not tested
let mut gen = Gen::new(async move |co| {
    co.yield_(10).await;
    co.yield_(20).await;
});
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Complete(()));

Using an async closure faux·sure (works on stable Rust)

let mut gen = Gen::new(|co| async move {
    co.yield_(10).await;
    co.yield_(20).await;
});
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Complete(()));

Passing ordinary arguments

This is just ordinary Rust, nothing special.

async fn multiples_of(num: i32, co: Co<i32>) {
    let mut cur = num;
    loop {
        co.yield_(cur).await;
        cur += num;
    }
}

let mut gen = Gen::new(|co| multiples_of(10, co));
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Yielded(30));

Passing resume arguments

You can pass values into the generator.

Note that the first resume argument will be lost. This is because at the time the first value is sent, there is no future being awaited inside the generator, so there is no place the value could go where the generator could observe it.

async fn check_numbers(co: Co<(), i32>) {
    let num = co.yield_(()).await;
    assert_eq!(num, 1);

    let num = co.yield_(()).await;
    assert_eq!(num, 2);
}

let mut gen = Gen::new(check_numbers);
gen.resume_with(0);
gen.resume_with(1);
gen.resume_with(2);

Returning a completion value

You can return a completion value with a different type than the values that are yielded.

async fn numbers_then_string(co: Co<i32>) -> &'static str {
    co.yield_(10).await;
    co.yield_(20).await;
    "done!"
}

let mut gen = Gen::new(numbers_then_string);
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Complete("done!"));

Structs

Gen

This is a generator which can be shared between threads.

Type Definitions

Co

This object lets you yield values from the generator by calling the yield_ method.

GenBoxed

This is a type alias for generators which can be stored in a 'static. It's only really needed to help the compiler's type inference along.