The concept of coroutines (Melvin E. Conway in 1963; who was also the source of FORK/JOIN) predates by a decade that of iterators/generators (which come from Alphard and CLU).
The term "yield" had already been used for many years for coroutines before being used for iterators/generators.
Usually the use of "yield" is not ambiguous, because the context is clear.
https://learn.microsoft.com/en-us/archive/msdn-magazine/2019...
> Enforcing safety seems like the right call
It certainly is, however it is hard to argue this point while looking at other more or less recent features:
string_view is basically a fancy "char *", with all the same use-after-free issues (same with other pointer range types and iterators)
Lambas with capture-by-reference can be freely copied and type erased into function<>, so obviously they did not care about safety when that feature got included
That said, span (unlike string_view) fails at its intended purpose, because C++ just loves to be a total clown show.
Because of the performance? Or for another reason?
The days of avoiding malloc in games is long since passed. Of course, fewer allocations, like reducing any work, is always better.
I think the conflict is between "yielding another value" and "yielding control back to the scheduler". Both of them use "yield" but they're different types of yield, and people use the same keyword for both, and either one can be implemented in terms of the other, and so on.
Still, I don't think iterators and coroutines are necessarily the same. You can imagine a coroutine that doesn't return a value and only happens to share a thread or something.
> Even weirder to say its a "C# coroutine" feature when there's no coroutine in the C# language.
Unity has a coroutine system based on iterators, maybe that's where the confusion is coming from:
https://docs.unity3d.com/Manual/Coroutines.html
...or simply because async/await, coroutines or any other multitasking system serves the same purpose for the user: to kick off background tasks.
A C# async method is also a coroutine.
Unity (and probably others) use yielding iterators as a somewhat hacky way to make coroutines, though. I wouldn't say its a good role model but it gets the job done.
> Give me access to the generated struct. Allow me to put it on the stack of another function. Or as a member of another struct. Then I can store it on the heap if I want, but don’t force me.
As for why they're allocated on the heap...seems like the stack would be a foot gun, no? To write a safe coroutine, you need to hope your caller doesn't blow out the stack between iterations or just defensively put everything on the heap yourself anyway. Enforcing safety seems like the right call but that's a matter of taste, I suppose.
This does remind me of a recent C# change to the Task<T> promise type returned by async/await functions. Before they were only heap allocated because you need that to be able to properly await them multiple times. This is a major reason you can't use them in games and use things like Unity's Coroutines. As it turns out a lot of small heap allocs suck and stack allocation does come in handy for perf. Now we have stack allocated Task<T> types as well. It would be a nightmare if the unsafe was the default though... Why is life so complicated?