A question on the convenience functions for coroutines in the standard library

Hello everyone!

Recently I was experimenting with an unstable Rust feature called coroutines. A coroutine can be thought of as a function that can both yield (temporarily interrupt its execution and return to caller to be resumed later) and return completely (like an ordinary function). It's similar to async fn functions, but async functions yield when the job cannot be completed right now because some external event hasn't happened yet, while coroutines yield when a job is partially done or when continuing requires a caller to supply some data. Another difference is that async/await is stable, while coroutines are not.

I wonder, where can coroutines be used? As far as I see, coroutines may be seen as an extension of an iterator interface -- specifically, on each step, a coroutine may both accept and yield a value (while iterators only yield values). There are other differences, however:

  • A coroutine may have a final "return value", while an iterator always finishes iteration with Option<T>::None
  • Any iterator must be prepared to be moved (since the Iterator trait uses a &mut self reference), while a coroutine may be non-movable (since the Coroutine trait uses a Pin<&mut Self> reference)
  • An iterator may expect to be polled after completion (it's not a logic error), and there is a fuse() method that prevents such polling (for example, if an iterator would panic if next() is called after an iteration is finished). Polling a coroutine after completion is a logic error, so a coroutine can expect not to be polled after returning CoroutineState::Complete (but not cause undefined behavior)

An Iterator trait contains a lot of convenience methods and adapters that make working with iterators easier, while there doesn't seem to be anything equivalent for coroutines (like filtering, mapping, folding, searching, linking two coroutines together). I wonder, are there crates for that, and are the plans to enhance coroutine support in the standard library?

I believe most of the interest in implementing coroutines is as the foundation of async blocks and generators (coroutines acting as iterators), which each have their own helpers.

Do you mean the core::iter::from_coroutine() method and its requirements (both argument and return types are () and the coroutine implements Unpin (moveable or boxed)?

I believe kpreid is referring to gen blocks:

An additional difference between iterators and coroutines is that coroutines accept an additional argument when they are resumed. Continuing the analogy with futures, that argument for them is the current context carrying the waker.

You can have many traits and variations with all these differences that can more or less flexible but also more or less complex. Overall however I find coroutines to be useful because the compiler provides syntactic sugar for generating one given some more normal looking code, similar to async fns. This allows you to more intuitively express some state machines that normally you would have to write manually.

/me sighs