liveBook · Manning

Coroutine

As explained in Idiomatic Rust

A coroutine is a function-like computation that can suspend its execution, yield values, and later resume or complete.

A coroutine is a pausable computation: it can yield values to its caller, suspend, then resume or finish with a return value. Rust’s coroutine feature is experimental and nightly-only. section 8.2

Rust coroutines make it possible to:

  • Separate values yielded during execution from the final return value. section 8.2
  • Express stream iteration, cooperative multitasking, and event loops without relying on threads or callbacks. section 8.2
Listing 8.5 std::ops::Coroutine trait definition from Rust standard library
pub trait Coroutine<R = ()> {     #1
    type Yield;                     #2
    type Return;                   #3

    // Required method
    fn resume(
        self: Pin<&mut Self>,      #4
        arg: R
    ) -> CoroutineState<Self::Yield, Self::Return>;
}

The Coroutine trait’s resume method takes a pinned mutable reference and reports either a yielded value or completion. section 8.2

Listing 8.6 A basic coroutine in Rust
#![feature(coroutines,               #1
           coroutine_trait,      #1   
           stmt_expr_attributes)]   #1

use core::f64::consts::PI;
use std::ops::{Coroutine, CoroutineState};
use std::pin::Pin;


fn main() {
    let mut yield_pi = #[coroutine]      #2
    || {                                     #3
        yield PI;                          #4
        "Coroutine complete!"     #5
    };

    loop {
        match Pin::new(&mut yield_pi).resume(()) {     #6
            CoroutineState::Yielded(val) => {      #7
                dbg!(&val);
            }
            CoroutineState::Complete(val) => {     #8
                dbg!(&val);
                break;
            }
        }
    }
}

A feature-gated coroutine yields π, then completes with a string, showing the two value paths in a small example. section 8.2

Listing 8.7 Implementing Iterator atop a coroutine
struct CargoTomlReader {
    coroutine:
        Pin<Box<dyn Coroutine<          #1
            Yield = (usize, String),    #1
           Return = ()                  #1
        >>>,                            #1
}

impl CargoTomlReader {
    fn new() -> io::Result<Self> {
        let file = File::open("Cargo.toml")?;
        let mut reader = BufReader::new(file);
        let mut line_number: usize = 0;          #2

        let coroutine = Box::pin(
            #[coroutine]                #3
            move || loop {                         #4
                let mut line = String::new();
                line_number += 1;                       #5
                match reader.read_line(&mut line) {
                    Ok(0) => return,                     #6
                    Ok(_) => yield (line_number, line),   #7
                    _ => return,                #8
                }
            }
        );
        let coroutine = Box::pin(
            #[coroutine]
            move || loop {
                let mut line = String::new();
                line_number += 1;
                match reader.read_line(&mut line) {
                    Ok(0) => return,
                    Ok(_) => yield (line_number, line),
                    _ => return,
                }
            },
        );
        Ok(Self { coroutine })
    }
}

impl Iterator for CargoTomlReader {
    type Item = (usize, String);
    fn next(&mut self) -> Option<Self::Item> {
        match self.coroutine.as_mut().resume(()) {      #9
            CoroutineState::Yielded(val) => Some(val),
            CoroutineState::Complete(()) => None,
        }
    }
}

A coroutine-backed reader yields numbered lines from Cargo.toml and is used as an iterator. section 8.2

How Idiomatic Rust covers it

How deep it goes
Explains yielding versus return, resumption through the Coroutine trait, compiler-generated state-machine internals, and a stream-reader example.
Examples
Demonstrates yielding π before a string return, then iterating over numbered lines read from Cargo.toml.
What you can do after
Build it from scratch by writing a nightly coroutine that yields values or adapts a line-reading stream into iteration.
How it connects
Connects coroutines to state machines, iterators, concurrency, event loops, and Python generators.

Also explained in

Compare how 8 books explain coroutine →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Idiomatic Rust. The section links point to the parts of the book it draws on.