What precisely does a placeholder lifetime mean in an async closure signature?

โš“ Rust    ๐Ÿ“… 2026-03-21    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 1      

surdeus

Let's say I have a struct like this that accepts a lifetime parameter:

struct S<'a>(&'a str);

What does the lifetime parameter mean in this closure?

async |_: S<'_>| { todo!() }

Given that underneath this expands to an async_call_mut method, I thought maybe it meant that the S parameter needs to have the same lifetime as the closure borrow. But I guess that doesn't make sense in the context of AsyncCallOnce, and this program compiles fine despite the closure not being 'static (playground):

struct S<'a>(&'a String);

fn check_accepts_static<F>(_: &F)
where
    F: AsyncFnMut(S<'static>),
{
}

fn main() {
    let f = async |_: S<'_>| todo!();
    check_accepts_static(&f);

    let s = String::from("taco");
    f(S(&s));
}

So I now suspect that what's going on here is that the closure is generic over the lifetime associated with S. I can't find any references on thisโ€”the internet says that closures cannot be generic, but perhaps that means "aside from lifetimes".

Is it accurate to say that there is not one AsyncFnMut implementation, but a family of them, and they wind up looking something like this (particularly with respect to the lifetimes and their relationships to each other)?

struct SomeClosure;
struct FnMutFuture<'c, 's>(&'c mut SomeClosure, S<'s>);

impl<'s> AsyncFnMut<(S<'s>,)> for SomeClosure {
    type CallRefFuture<'c> = FnMutFuture<'c, 's>;

    extern "rust-call" fn async_call_mut(&mut self, args: (S<'s>,)) -> Self::CallRefFuture<'_> {
        FnMutFuture(self, args.0)
    }
}

(Playground)

I really wish there were a way to get the compiler to dump something like this for its own generated implementations, but I can't find one.

2 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed