-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
eRFC: Experimentally add coroutines to Rust #2033
Conversation
This is an **experimental RFC** for adding a new feature to the language, coroutines (also commonly referred to as generators). This RFC is intended to be relatively lightweight and bikeshed free as it will be followed by a separate RFC in the future for stabilization of this language feature. The intention here is to make sure everyone's on board with the *general idea* of coroutines/generators being added to the Rust compiler and available for use on the nightly channel.
The only thing I can see that I don't immediately agree with is the "coroutine" terminology 😆 (I've gotten used to the ES "generator" vs "coroutine" dichotomy which is stackless vs stackful), all the non-fundamental constructs I had doubts about, from other RFCs, seem to be gone. We have to be careful to avoid stabilizing a type like It'd be interesting to see how far people can get with emulating actual generators on top of plain |
One nit I have, in the cleanliness/ergonomics domain - the example you give is an
Connected with this, what conversions should EDIT: Ah, I missed that you do specify the latter desugaring, though I think a clarification for EDIT 2 (Electric Boogaloo): One thing I do feel should be brought up is a specific section of this post by Joe Duffy about using asynchrony in Midori, and specifically using coroutines with async/await syntax sugar:
It may not be reasonable to insist on having a story for this right away, but I think there should at least be a story on having a story for it 😛 |
Sharing an |
(Emphasis added.) It’s easy to overlook the latter part of the quoted sentence. I’d like the RFC to say that a path exists to eventually stabilize corountines (subject to future discussion and RFCs) for use cases other than async/await, for example for returning |
@arielb1 Ah, indeed, with the ability to put in a value and later take it out, |
text/0000-experimental-coroutines.md
Outdated
solving our problem! | ||
|
||
Coroutines are, however, a little lower level than futures themselves. The | ||
stackless coroutine feature can be used not only future futures but also other |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/future /for /
So the RFC proposes to add stackless coroutines as experimental feature, but doesn't really describe what stackless coroutines would look like, and what the advantages of stackless coroutines are over stackfull ones. I'd like more on that, especially as I'm not certain how this RFC manages to avoid the issue of stackless coroutines that yielding is only possible from the top level coroutine. Is it the state machines? |
I'm definitely in favour of this feature, although I'm still hoping that a generic "execution context" parameter is added to futures, similar to rust-lang/futures-rs#129, to replace the scoped thread-locals which are currently used. This would require the At the risk of encouraging further bike-shedding, would it be possible to add the details of the new types and traits to the RFC? |
Do we really need the |
@ihrwein The problem there is "What if you want to await on a future, but not early-return on error?" Catching On the other hand, I would be curious about making |
@eternaleye That makes it clear, thanks. I believe if this pattern will be very common (await + ?), then ? could be extended to work with futures (although I see that they are somewhat orthogonal). |
I would like to see iterators made more of a focus here. I don't necessarily see making them a priority as more burdensome than making async/await work, since we essentially already have to. Our story for writing complex iterators from scratch is abysmal; I'd prefer us to not have to wait for another RFC/implementation/nightly cycle after this one. @est31 |
I just wanted to make a meta-comment about this RFC and the role that I see it playing. In discussions on internals and elsehwere, we've been going back and forth for some time about the best way to handle complex language extensions like coroutines, syntax extensions, embedded Rust support, and so forth. These are basically all cases where there seems to be a clear high-level need, but there is also a very large "design space" to explore. In such cases, it's not clear that writing up a detailed RFC is the best place to start. Often, a lot of the interesting questions only arise when you start putting the design into practice. Furthermore, even if we could write a big RFC, everybody is busy, and it's not clear that the various teams will have the time to really vet and approve something that complex. We've handled this in the past in a number of ways. Sometimes we can write up a kind of "conservative" RFC that describes a limited form of the feature (e.g., for To better handle these cases, I'm hoping we can kind of formalize an "experimental RFC" process. The idea is that we'll start with an "experimental RFC" like this one, that is primarily aimed at explaining the motivation and also a high-level sketch of what we plan to do. So, in this case, the motivation is that async-await is needed, and the high-level plan is that we hope to build it outside of the language, using two more fundamental features: procedural macros and some form of coroutine/generator syntax to do the state-machine transformation. The goal of this RFC is then to evaluate that high-level strategy and affirm that we think it's a promising path to explore. As such, I don't imagine that this RFC will require as much deliberation as other RFCs, since it's not making any kind of commitments or firm decisions. It's good for the RFC to also identify some of the questions that we intend to answer as we go, but we shouldn't expect it to have answers. Examples might be: what precisely should the async-await macros look like? How exactly will the coroutines be surfaced in Rust syntax, and what traits etc will they use? Those conversations can take place later, and as we come to conclusions, we should track the answers and be working on follow-up RFCs which will address them in particular. The key point then is that the experimental RFC is not enough to stabilize something. For that, we need a proper RFC that lays out the plan. This RFC can be informed by the experience and working implementation, so we should have a lot of data to use. Now, in a sort of meta-meta twist, I don't know how well this process will work. I view this experimental RFC as itself an experiment in process. The main thing I want to avoid is that we wind up with a lot of features that "work" but don't have much for a formal spec. We have enough of that grandfathered in already. ;) This is why I wanted to ensure we have a follow-up RFC, and I think we should be careful that when we land patches on "experimental code", we insist that it comes with good, well-organized test cases that document the behavior and the edge cases. In other words, just because something is experimental doesn't mean it has an excuse to be sloppy. (Ideally, we'd be trying to maintain some informal documentation in the nightly book, as well -- but I think this applies to all the nightly features.) The other concern of course is that we're going to be maintaining this code in master. It will interact with the rest of the system. It will cause regressions. In a way, this is a real strength: one of the things we want to be evaluating about a new feature is how it interacts with the rest of our system! But it's also a reason that we should all decide whether to take on that maintaince burden. |
I'd prefer if you could distinguish between "this might return in an error condition" and "this is an async break point". If the |
@eternaleye Hm... How about this? Calling |
Now, in a non-meta comment, I'm very much in favor of this RFC and this general approach. It seems to me that "coroutines/generators" are the right fundamental primitive to start with; they can support so many different applications (iterators, as @camlorn points out, but also other "future-like" traits, as well as thinks like parsers and lexers). Having async-await work via syntax extension feels good to me since it puts all of these other kinds of applications on equal footing; they may want their own "wrapper" macros or types. I like that as a side-benefit of this work we will be improving syntax extensions, and in particular I imagine there will be a good focus on their error messages and so forth, since we'll want to make (One other observation: I think that our experience with A few responses to some comments I saw:
I do think these are good questions to raise, though I think that we don't have to settle them by any means to accept the RFC. In other words, figuring out the most ergonomic way to encode async-await feels like the kind of thing that we can do during the implementation period, and I wouldn't want this RFC to try and do it. But maybe it'd be good to start an internals thread or some other place to hammer out these details. (For example, another thing that occurs to me is that, since
This does seem like something that might be good to talk about, since choosing to build on stackless coroutines is a key building block here. I think one of the obvious advantages of stackless coroutines is that they don't really require any sort of "runtime", or at least they push that decision outward. If you use something where we allocate a stack ahead of time, then you have to decide who is allocating the stack and manage it -- you also run into some hard questions about what to do if the stack is not big enough, and so forth. I think @vadimcn's stackless coroutines RFC has a bit more here, although it doesn't talk as much about it.
I'm not sure if I think that should be in the RFC -- at minimum, I'd want to clearly label them as non-normative. But it's probably a good idea to try and write-up that description, perhaps posting it into the internals thread on the topic for more discussion. I also think that the we ought to document these things, at least in some form, in the unstable book in @Zoxc's branch. @camlorn
I do think that's a great area to dig into, though I'd rather do it in a specific thread on the topic than here, since it's really a question of working out the fine details -- it's clear it will work in some form or another. For example, @vadimcn's RFC #1823 proposed blanket impls of
That's really slick, but this precise approach will likely run afoul of the existing coherence rules (although I just remembered that the (By the way, I think this is another good example of where the experimental process would help. That is, once we have the basic system implemented, we can make branches that experiment with various approaches around iterators and see what happens, in terms of its impact on existing impls etc.) |
This sort of thing also appeared in the |
First of all, I want to express how excited I am to see all the progress that has been made on this. It seems like it's being done in a methodical and careful way. I'm interested to see what we learn from having experience using this feature on nightly. One nit: I don't see any explicit mention in the RFC of why the |
@alexcrichton I have bad feelings about the use of procedural macros for features like asynchronicity/generators/coroutines where keywords may be expected. Isn't this starting to make Rust really over-complicated for a supposed flexibility? I mean it's somehow providing the illusionary impression that the number of keywords is constant whereas - as I see it - it is in fact splitting the keyword space into two syntactical classes, one of them (procedural macros) being easier to extend. Aren't we creating another language inside the language? Maybe I haven't realised before this was the point of procedural macros to begin with. Am I the only one worried about this kind of consideration? |
Ok thanks for all the initial comments everyone! I've pushed an update with two pieces:
I believe the alternative is: impl<T: Coroutine> Future for T {
// ...
} (or something like that) I'd personally be very wary of such an impl in the I added an "open question" about this though in how the traits interact. This question/problem I'd expect is far more relevant to iterators, for example, as the translation between coroutines an iterators is in theory much simpler.
A good point! I've made sure to register this as an open question to make sure we loop back around to. For now though my hope is to keep this RFC as minimal as possible to impact on the compiler and make it as easy as possible in terms of maintenance burden to land in the compiler. Along those lines the choice of new keywords isn't taken for now. It's worth pointing out two imporant aspects, though. The historic The second point though is along the lines of what @nikomatsakis mentioned in his first response to @eternaleye. The Basically, I think the choice of syntax extensions early on provides us a maximal amount of flexibility moving forward. We can switch to keywords later, we can experiment with keywords right now. Lots of open possibilities! |
If the point of a experimental RFC is that it must be followed up by a proper RFC that lays out the details omitted by the eRFC would it make sense to have an Deferred Questions section, both to document what the the final RFC needs to cover and to be transparent about why the eRFC doesn't touch upon some topics? |
@johansigfrids The second half of the "Detailed design" section already is that list of questions. Are you saying there are some other questions that need to be added to it or that the list needs to be moved into a separate section to draw even more attention to it or something? |
I'm suggesting it would be a separate section as a sibling to Unresolved Questions to make it more clear. That the open questions mentioned in design are deferred to final RFC is currently hidden inside the text. |
Sorry to bother you guys, just here to ask whether or not I'm currently trying to developing a website using Rust, but I found that some third-party external crates I needed does not support Async IO, which really hurts server performance a lot. Consider it's really really hard to ask all crate authors to uniformly use something like |
Just ask them to adopt tokio or something based on futures-rs. Those two are the standard. Optionally you can ask them to add an async mode so that people are not forced to pull in tokio or futures-rs. |
Well, I must getting something wrong. I did a little research on the context and found that you guys are more leaned towards reduce the content that can be put into the standard library for some reason, maybe that's alright, but some features can really work better in the core. Some language already did that (to use standard library to introduce standards, not just put essential code), and they work wonderfully like a dream. Actually, if you look real close, Rust sort if did it too, But you guys still put them in there, presumably to provide something standard so everybody can be on the same table and be little happier? What would happen if those things does not existed in the Rust is a hard language already, please do something to make user's life easier. Bonus question: Why do people in the PHP world tend to build their project on top of a full stack framework rather than compose a custom framework by themselves, even they knew a full stack framework is often slower than the composed one? |
@Reinit I'm afraid if you're looking for a production-ready async system with strong backwards compatibility guarantees, that's just not available yet. Moving it into the standard library won't help in that regard. |
@Diggsey OK, then looks I need to wait a little while. Thank you. |
Hi, Is it now what you are waiting for? I'm a newbie for Rust, I'm browsing this thread and don't know if the current Rust world has reached an agreement on the concepts and implemtation. Tokio seems to reach to 1.0 |
It's a much different world in 2021 in Rust. |
This feature is amazing! Is it possible to 'inline' yielding to an outer coroutine? let k = || yield {
let d: u32 = a().coawait;
};
struct Thing;
fn a() -> impl Coroutine<Return = u32, Yield = Thing> {
|| {
yield Thing;
return 233;
}
} If yes, then this subsumes rust's default ugly async!! Do you think that it is possible? @alexcrichton |
Generator support This adds experimental support for generators intended to land once rust-lang/rfcs#2033 is approved. This is not yet ready to be merged. Things to do: - [x] Make closure arguments on generators an error - [x] Spot FIXMEs - [x] Pass make tidy - [x] Write tests - [x] Document the current syntax and semantics for generators somewhere - [x] Use proper error message numbers - [x] ~~Make the implicit argument type default to `()`~~
This comment was marked as duplicate.
This comment was marked as duplicate.
@VVishion is that rust-lang/rust#68923? |
@shepmaster Oh, yes it is. Thanks! |
Please make this a standard feature! |
This is an experimental RFC for adding a new feature to the language,
coroutines (also commonly referred to as generators). This RFC is intended to be
relatively lightweight and bikeshed free as it will be followed by a separate
RFC in the future for stabilization of this language feature. The intention here
is to make sure everyone's on board with the general idea of
coroutines/generators being added to the Rust compiler and available for use on
the nightly channel.
Rendered
What's a experimental RFC?