Stackless vs Stackful Coroutine
Two implementation forms for coroutines, differing in how they preserve execution state across a suspend.
Why does it matter?
The implementation form dictates what the coroutine can suspend from. A stackless coroutine can only suspend at the top level of its main routine — you can’t
suspend()from inside a helper function it calls. A stackful coroutine can suspend anywhere in its call tree, which is what makes it suitable for things like recursive tree iteration.
Stackless
Uses the caller’s stack and a fixed-sized local state (usually a struct / compiled state machine).
- Cheap — no separate stack allocation
- Can only suspend at top-level points in the coroutine body (the compiler must know where to save state)
- C++20
co_await, Python generators, Rustasync fnare all stackless
Stackful
Uses a separate stack plus a fixed-sized local state (as part of the coroutine object).
- Heavier — every coroutine instance allocates its own stack
- Can suspend from anywhere, including deeply nested helper calls (see the
Btree::Iteratortree-walk in Coroutine) - uC++
_Coroutine, Go goroutines, Lua coroutines are stackful
Why uC++ is stackful
uC++‘s tree-traversal iterator calls walk() recursively and suspend()s from inside a leaf — that’s impossible with stackless because the recursive frames live on the active call stack. Stackful pays the per-instance stack cost to buy that flexibility.