Description
I've noticed exponential increases in compilation times as I nest async closures within each other.
I tried this code:
macro_rules! compose_middleware_inner {
( $route:ident, $first:ident, $second:ident, $($tail:ident), +) => {
$first(|| async {
compose_middleware_inner!($route, $second, $($tail),+)
}).await
};
( $route: ident, $first:ident, $second:ident ) => {
$first(|| async move { $second($route).await }).await
};
}
macro_rules! compose_middleware {
( $name:ident, $($tail:ident), +) => {
pub async fn $name<N, Fut>(route: N)
where
N: FnOnce() -> Fut,
Fut: std::future::Future<Output = ()>,
{
compose_middleware_inner!(route, $($tail),+)
}
}
}
async fn log<N, Fut>(next: N)
where
N: FnOnce() -> Fut,
Fut: std::future::Future<Output = ()>,
{
println!("log start");
next().await;
println!("log end");
}
compose_middleware!(
my_middleware, log, log, log, log, log, log, log, log, log, log, log, log, log
);
That compose_middleware!
macro invocation generates a function that looks something like this:
pub async fn my_middleware<N, Fut>(route: N)
where
N: FnOnce() -> Fut,
Fut: Future<Output = ()>,
{
log(|| async { log(|| async { log(|| async move { log(route).await }).await }).await }).await
}
I expected to see this happen: I expected this code to build within seconds
Instead, this happened: At around 3 levels it takes less than a second to build. At around 9 levels it takes around a minute to build. At around 14 levels it takes around 10 minutes to build.
Meta
I've experienced this issue on nightly, stable, and beta. And I've sampled random nightly versions going back as far as 1.39 and still witnessed the problem. There was a good bit of variability in build times, but it generally seemed to increase exponentially.
I threw this little repo up to test the issue: https://github.com/blazzy/slow-rust-async/blob/master/src/lib.rs
I thought it might be related to this issue #72408 with nested closures. Or this issue #75992 with levels of async, but those look to be resolved.