Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick start

I see you’re curious of how coroutines looks like in Rust - let’s feed you some minimal example!

Installation

Add Moirai to the project:

cargo add moirai

Minimal code

First we create jobs runtime to spawn coroutines into:

use moirai::{coroutine::yield_now, job::JobLocation, jobs::Jobs};
use moirai_book_samples::{
    game::{Game, GameState, GameStateChange},
    utils::quit_if_jobs_completed,
};
use std::time::Duration;

fn main() {
    let jobs = Jobs::default();
    Game::new(Example { jobs }).run_blocking();
}

struct Example {
    jobs: Jobs,
}

impl GameState for Example {
    fn enter(&mut self) {
        self.jobs.spawn(JobLocation::Local, async {
            let mut counter = 0;
            for _ in 0..10 {
                counter += 1;
                // Yield execution to allow other jobs to run.
                yield_now().await;
            }
            println!("Counter: {:?}", counter);
        });
    }

    fn frame(&mut self, _delta_time: Duration) -> GameStateChange {
        self.jobs.run_local();

        quit_if_jobs_completed(&self.jobs)
    }
}

Then we spawn some job somewhere in game:

use moirai::{coroutine::yield_now, job::JobLocation, jobs::Jobs};
use moirai_book_samples::{
    game::{Game, GameState, GameStateChange},
    utils::quit_if_jobs_completed,
};
use std::time::Duration;

fn main() {
    let jobs = Jobs::default();
    Game::new(Example { jobs }).run_blocking();
}

struct Example {
    jobs: Jobs,
}

impl GameState for Example {
    fn enter(&mut self) {
        self.jobs.spawn(JobLocation::Local, async {
            let mut counter = 0;
            for _ in 0..10 {
                counter += 1;
                // Yield execution to allow other jobs to run.
                yield_now().await;
            }
            println!("Counter: {:?}", counter);
        });
    }

    fn frame(&mut self, _delta_time: Duration) -> GameStateChange {
        self.jobs.run_local();

        quit_if_jobs_completed(&self.jobs)
    }
}

In game loop we run pending local jobs queue, usually at the end of game frame:

use moirai::{coroutine::yield_now, job::JobLocation, jobs::Jobs};
use moirai_book_samples::{
    game::{Game, GameState, GameStateChange},
    utils::quit_if_jobs_completed,
};
use std::time::Duration;

fn main() {
    let jobs = Jobs::default();
    Game::new(Example { jobs }).run_blocking();
}

struct Example {
    jobs: Jobs,
}

impl GameState for Example {
    fn enter(&mut self) {
        self.jobs.spawn(JobLocation::Local, async {
            let mut counter = 0;
            for _ in 0..10 {
                counter += 1;
                // Yield execution to allow other jobs to run.
                yield_now().await;
            }
            println!("Counter: {:?}", counter);
        });
    }

    fn frame(&mut self, _delta_time: Duration) -> GameStateChange {
        self.jobs.run_local();

        quit_if_jobs_completed(&self.jobs)
    }
}

As you see, this is bare minimum needed to process coroutines in your game. Just a runtime running local jobs at the end of a game frame, and a spawned jobs in it - that’s all!