Skip to main content

Crate diapause

Crate diapause 

Source
Expand description

Coroutines/generators via code transformation, without async.

Annotate a function with coroutine and it is rewritten into a state machine implementing the Coroutine trait. Suspension points are written as yield_!(expr); the body is analyzed as a control-flow graph and rewritten at compile time — no async, no allocation, no unsafe code.

use diapause::{Coroutine, CoroutineState};

#[diapause::coroutine(yield = u32, resume = u32)]
fn running_total(start: u32) -> u32 {
    let a = yield_!(start);
    let b = yield_!(start + a);
    start + a + b
}

let mut c = running_total(100);
assert_eq!(c.start(), CoroutineState::Yielded(100));
assert_eq!(c.resume(1), CoroutineState::Yielded(101));
assert_eq!(c.resume(2), CoroutineState::Complete(103));

Calling the annotated function returns the initial state without running any code; Coroutine::start runs the body up to the first yield_!, and each Coroutine::resume continues from the previous suspension point, passing its argument as the value of the let x = yield_!(..) binding.

The playground runs the transform in the browser, showing the expanded code and control-flow graph for any annotated function.

§Supported bodies

yield_! works inside if / match / loop / while / while let / for at any nesting depth, mixed with break, continue, early return, and the ? operator on Result and Option. yield_all! delegates to another coroutine, forwarding its yields and resume values (yield_all_resume! does the same for one that is already started, and the box modifier stores the delegate boxed, enabling recursion). Because the state enum stores only concrete types, serde derives work with their ordinary semantics and a suspended coroutine can be serialized, deserialized elsewhere, and resumed — nested delegation states included.

The macro never sees rustc’s type information and works purely syntactically, which imposes rules on the body: yield_! is statement-position only, variables held across a suspension point need syntactically determinable types, and only direct borrows may cross a yield. See coroutine for the full constraint list with workarounds.

§Comparison with async-based generators

Crates like genawaiter implement generators by driving an async block. diapause instead generates the state machine itself, which means no Pin (states are always Unpin), resume arguments that are plain function arguments rather than shared-cell tricks, an inspectable state enum that supports #[derive(Clone)] snapshots and serde persistence — at the price of the syntactic rules above.

Macros§

yield_
Marks a suspension point inside a #[diapause::coroutine] function.
yield_all
Delegates to another coroutine inside a #[diapause::coroutine] function (the analogue of Python’s yield from).
yield_all_resume
Delegates to an already-started coroutine inside a #[diapause::coroutine] function.

Structs§

FingerprintMismatch
A persisted coroutine state does not match the source of the coroutine it is checked against.
Iter
A wrapper that implements Iterator for a coroutine.

Enums§

CoroutineState
The result of a Coroutine::start / Coroutine::resume call.
CoroutineStatus
The result of a Coroutine::status query.

Traits§

Coroutine
A resumable computation.
Fingerprinted
Fingerprint validation of a persisted coroutine state.

Attribute Macros§

coroutine
Transforms a function into a coroutine state machine.