Are you sure about that? One of the main advantages of coroutines/greenlets IMO is writing simple and straightforward blocking code (e.g. an echo server); without them, you either need to use threads (which are slower and much more heavyweight) orcallbacks or related constructs (async/await, futures, ...).
Even if it could, many real-world servers actually do non-trivial work in their threads, so the cost of spawning a thread is dwarfed by the actual work the thread ends up doing. There are serious drawbacks to M:N: complexity, fairness, problems in interoperability with the 1:1 world (including essentially unavoidable performance problems with the FFI), etc.
There is a Rust library called "mio" which provides a lot of the plumbing for such systems: https://github.com/carllerche/mio.
The path forward is likely going to involve adding a way to build cheap state machines (call them generators or async/await) with a clean syntax and giving mio hundreds of thousands of reusable instances.
I don't understand, and the link seems unclear. Perhaps a more direct question: I get a request X, and I need to consult a backend service to answer the request. Do I write synchronous code calling that backend? Or do I have some callback mechanism?
> ... generators or async/await
Ah. This perhaps answers my question. Both of these are essentially compiler-written callbacks.
If this is going to be like C#, then I presume there will be a thread-pool where user code will execute. It seems like a non-ideal story for concurrency. Users will have to take inordinate care not to call any blocking code; otherwise they will prevent one of the threads in the pool from doing useful work.
The downsides of going M:N are worse. The cgo-like FFI performance problems, for example, are killer for Rust's use case.
To be honest it seems to me like your explanation is an attempt to downplay just how nice fibers/coroutines are rather than acknowledge their utility in many existing languages.
Userspace is not equipped to make reasonable scheduling decisions that provide any significant performance advantage, and library/language runtime control of M thread register/stack contexts on top of N kernel threads plays absolute havoc with most operating system's standard libraries.
Go works around this by explicitly not calling into libc et al -- all system calls are issued directly. One big problem with that: directly invoking syscalls is supported on Linux, but NOT supported on OS X.
End result is that Go literally must rely on undefined behavior on any system that does not support direct issuing of syscalls.
From my brief review just now, what MS appears to be proposing for C++17 isn't coroutines in the traditional M:N threading sense, but rather, an explicit mechanism (with syntactical sugar) for capturing reachable variables in a lambda (without preserving the stack), and issuing a call to that magicked-up lambda later via promises.
This is interesting if you love the idea imperative mutable promise-based concurrency, but it's not likely to win you any performance gains, and it's useless in the extreme if imperative mutable promises aren't your cup of tea.
But since Rust is intended for runtime-less lower-level programming it doesn't make any sense here.