Size of `async fn` futures

⚓ Rust    📅 2026-01-16    👤 surdeus    👁️ 8      

surdeus

Info

This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Size of `async fn` futures

Background: I'm making a testing instrumentation crate for testing, and I'm wrapping Futures in functions that monitor their execution, and I want the wrapper function to be zero-cost when instrumentation is disabled.

I've noticed that Futures produced by async fns take up more space than I expected, and their size grows exponentially rather than linearly in their nesting depth.

I've ran this code:

async fn foo(inner: impl Future<Output = ()>) {
    inner.await;
}

fn bar(inner: impl Future<Output = ()>) -> impl Future<Output=()> {
    Bar { inner }
}

struct Bar<F> {
    inner: F
}

impl<F: Future> Future for Bar<F> {
    type Output = F::Output;
    
    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context) -> std::task::Poll<Self::Output> {
        let inner = unsafe { std::pin::Pin::new_unchecked(&mut self.get_unchecked_mut().inner) };
        inner.poll(cx)
    }
}

fn main() {
    println!("sizeof async{{}} = {}", std::mem::size_of_val(&async{}));
    println!("sizeof foo(async{{}}) = {}", std::mem::size_of_val(&foo(async{})));
    println!("sizeof foo(foo(async{{}})) = {}", std::mem::size_of_val(&foo(foo(async{}))));
    println!("sizeof foo(foo(foo(async{{}}))) = {}", std::mem::size_of_val(&foo(foo(foo(async{})))));
    println!("sizeof foo(foo(foo(foo(async{{}})))) = {}", std::mem::size_of_val(&foo(foo(foo(foo(async{}))))));
    println!("sizeof bar(async{{}}) = {}", std::mem::size_of_val(&bar(async{})));
    println!("sizeof bar(bar(async{{}})) = {}", std::mem::size_of_val(&bar(bar(async{}))));
    println!("sizeof bar(bar(bar(async{{}}))) = {}", std::mem::size_of_val(&bar(bar(bar(async{})))));
    println!("sizeof bar(bar(bar(bar(async{{}})))) = {}", std::mem::size_of_val(&bar(bar(bar(bar(async{}))))));
}

and it prints the following (it's the same with and without optimizations):

sizeof async{} = 1
sizeof foo(async{}) = 3
sizeof foo(foo(async{})) = 7
sizeof foo(foo(foo(async{}))) = 15
sizeof foo(foo(foo(foo(async{})))) = 31
sizeof bar(async{}) = 1
sizeof bar(bar(async{})) = 1
sizeof bar(bar(bar(async{}))) = 1
sizeof bar(bar(bar(bar(async{})))) = 1

Why are futures produced by async fn so much larger than the equivalent manual implementation of Future?
Am I missing some optimization opportunities here?
Can I reduce the size of results of async fn without implementing Futures manually?

2 posts - 2 participants

Read full topic

🏷️ Rust_feed