Future-proof the Futures API by cramertj · Pull Request #59119 · rust-lang/rust (original) (raw)
I spent a good hour yesterday going over the document, trying to resolve comments. There are still a few comments left that I wasn't able to incorporate, but I think overall it's fairly representative. Still, the document is quite long by now. From reading it, I think the crux of the debate comes down to TLS. So let me take a shot at pulling out the "core tradeoff". I'm curious to hear what people think. Let me know if you don't feel represented here =)
The tradeoff
- First, on the one hand, adopting
Context
makes the trait more complex to understand (though how much is subjective). It also imposes an ergonomic cost (detailed in the doc). Let's call this the "ergonomic cost" of the PR. - On the other hand, unless we introduce
Context
, TLS is the best option for passing additional implicit state (see * below for a caveat), and TLS has downsides:- it is somewhat slower (though how relevant that is in the context of a full application is disputed, see the doc);
- it is implicit dataflow that can be brittle (so e.g. introducing threads could mess it up);
- it may not be portable to embedded platforms (though I think I'm convinced by now this is a non-issue, see the doc).
So the core tradeoff here is this:
- By leaving the design as is, we keep it as simple and ergonomic as it can be;
- but, if we wish to pass implicit parameters to the future when polling, we must use TLS.
Note that I emphasized passing implicit parameters. By this I mean data passed from the caller of poll
to the callee. It is also possible to thread additional data explicitly -- e.g., when creating the future, you could instantiate it with the data it will need during poll
. If you think of an async fn
, this roughly corresponds to passing the data to the async fn
as explicit arguments, versus the async fn
just 'magically' having access to it. (As noted in the doc, it's not clear how async fns would gain access to these additional implicit arugments without either using TLS or adding new keywords, unless they are used somehow in the desugaring of await
. This feels like an important point to me, too.)
The conclusion
So, which way you fall will depend on
- how important you think it is for
Future
to be ergonomic- and naturally how much of an ergonomic hit you believe this to be
- how likely you think it is for us to want to add implicit parameters
- how much of a problem you think it is to use TLS for those implicit parameters
I think a number of people feel that, by now, between Rust and other ecosystems, we have a pretty good handle on what sort of data we want to thread around and what the best way is to do it. Further, they feel that TLS or passing parameters explicitly is the best solution approach for those cases. Therefore, they prefer to leave the design as is, and keep things simple. (More details in the doc, of course.)
Others, however, feel like there is additional data they want to pass implicitly and they do not feel convinced that TLS is the best choice, and that this concern outweights the ergonomic costs. Therefore, they would rather adopt the PR and keep our options open.
Finally, I think there is a third position that says that this controversy just isn't that important. The performance hit of TLS, if you wind up using it, seems to be minimal. Similarly, the clarity/ergonomics of Future
are not as criticial, as users who write async fn
will not implement it directly, and/or perhaps the effect is not so large. These folks probably could go either way, but would mostly like us to stop debating it and start building stuff. =)
Footnotes
* -- One caveat is that there is another option besides TLS. We could add add'l state to the Waker. The document dives into the tradeoffs there. My opinion is that it seems to be (overall) a less appealing option than TLS, and certainly a less explored one in practice, so I chose to leave it off.