Hacker News new | ask | show | jobs
by tatterdemalion 2938 days ago
The user specifically asked about stackfull coroutines, which is not the direction we're going. But I think the pain point the user talked about (futures right now are difficult to deal with) will be resolved by our stackless coroutine approach, and more in line with Rust's values around "zero cost abstractions."
1 comments

That's fair. This space is quite complex, with so so many options. I guess we'll know if and when the OP elaborates with more :) I very well could be wrong.
You are both right. I was specifically asking about "stackfull coroutines" as tatterdemalion says, but I stackless coroutines with await/async would be as good for code readability.

Thanks for your work btw.

The difference between stackful and stackless is whether you can yield execution across a function that doesn't know about async/await.

The problem with stackless concerns code reuseability. With stackless you have to duplicate all your intermediate functions: once for code that calls functions or methods which can yield, and again for code that takes functions or methods which won't yield. Or you simply don't reuse code at all, bifurcating the entire ecosystem.

The stackful vs stackless effectively refers to whether the implementation uses the same stack discipline for calls that can yield vs those that cannot. In either case you're always going to have to construct some kind of stack to support nested function invocation, the question is whether you're going to duplicate all that infrastructure.

Also, it helps if you don't conflate asynchronous I/O with coroutines. Coroutines are a meta abstraction over functions (an abstraction over call chains) that can be used to create ergonomic async I/O, but have other uses, like inverting producer/consumer caller/callee relationships (e.g. converting a push parser into a pull parser with a couple of lines of wrapper code). Stackful coroutines reuse the normal call stack discipline; stackless coroutines require function annotations and compiler rewriting and lead to the code reuse problems mentioned above.

Stackful coroutines are actually a perfect fit for Rust's ownership model, and would simplify much of the work, or obviate it altogether. But for other reasons--C compatibility, poor OS constructs for minimizing memory use, and an unfortunate early conflation of coroutines with async I/O--Rust has chosen the path of stackless coroutines a la async/await as the official model.

No problem! To be clear, my work here is explaining it only; the implementation is entirely other people’s hard work :)
Explaining the right way is hard. And makes our learning less painful. Kudos!