post:
and zulipchat discussion link .
This post attempts to motivate some of the concrete technical reasons why the
#![feature(async_closure)]
that I (Michael Goulet) 've been working on recently[1][2][3][4] is so complicated. Specifically, why can't we just use|| async {}
that we can express on stable today, an interesting but very important note about the relationship between a "lending"FnMut
and theFnOnce
trait that I haven't seen written anywhere yet, and how async closures are a tractable solution to an otherwise intractable problem with lending closures.
tl;dr
- Async closures need to be lending, since they return futures that may borrow from the closure's captures.
- So they need to implement some
LendingFnMut
trait.- However,
LendingFnMut
is not easy to introduce into the currentFn->FnMut->FnOnce
trait hierarchy, becauseLendingFnMut
cannot be a subtrait ofFnOnce
. Why? Because:
- The value returned by a
LendingFnMut
implementation typically differs from that returned byFnOnce
, but they're inherited by the current subtraits.- So let's split out these two types – however, in general, there's no meaningful value for a lending closure to return when called in a way that consumes the closure.
- That is: lending closures typically do not implement
FnOnce
at all.- So we can't just fix
FnMut
and make it "just work".- However, Async closures do have an obvious
FnOnce
implementation that can be derived programmatically from theirLendingFnMut
implementation.- To simplify closure signature inference, it's easiest to stop using the most general definition (
LendingFnMut
) and specialize this trait for async – we introduceAsyncFn
/AsyncFnMut
/AsyncFnOnce
.- But to keep space for future generalization, we can hide this behind an
async
trait bound modifier.