Lifetime error with async closure when implementing tower::Service trait

I am trying make a small user authentication web-service as an exercise in learning tower::Service and axum.

I have abstracted away user credential storage via Fetch trait as follows

pub(crate) trait Fetch<T>: Debug + Send + Sync {
    type UserId;
    fn fetch(&self, user_id: Self::UserId) -> self::error::StoreResult<T>;
}

so I'd be able de-couple services from the underlying storage engine.

A UserFetch struct contains a concrete impl and a way to ID the spawned service

#[derive(Debug)]
pub(crate) struct UserFetch {
    id: Uuid,
    user_fetch: Arc<dyn store::Fetch<User, UserId = UserId>>,
}

StoreResult is just a util enum for Result<T, StoreError>.


This is how I am trying to impl tower::Service trait following their example in Service in tower - Rust

impl tower::Service<UserId> for UserFetch {
    type Response = User;

    type Error = service::error::ServiceError;

    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: UserId) -> Self::Future {
        Box::pin(async { Ok(self.user_fetch.fetch(req)?) })
    }
}

? to convert StoreError to ServiceError and Ok to re-wrap.

This is the error I get

   |
30 |     fn call(&mut self, req: UserId) -> Self::Future {
   |             - let's call the lifetime of this reference `'1`
31 |         Box::pin(async { Ok(self.user_fetch.fetch(req)?) })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`

I don't quite understand what the compiler is trying to say. Is it saying UserFetch the struct may not live as long as the async closure its method returns? What am I doing wrong here?

Yes, the signature of the trait allows the service to be destroyed before the call futures are, so the user_fetch arc could be dropped while the future exists.

To fix it, make a clone of the Arc before you create the future. This way, the arc is kept alive even if the service is destroyed.

fn call(&mut self, req: UserId) -> Self::Future {
    let user_fetch = self.user_fetch.clone();
    Box::pin(async move { Ok(user_fetch.fetch(req)?) })
}
5 Likes

Works perfectly! Thank you

I also tried something similar before, like this

fn call(&mut self, req: UserId) -> Self::Future {
    Box::pin(async move { Ok(user_fetch.clone().fetch(req)?) })
}

in my pursuit of one-liners :sweat_smile: but it showed the same error.

I am assuming because cloning Arc inside of the async block doesn't grantees struct's availability. It could've been dropped before the async block could execute and clone the Arc?

Yes, that is the reason why your code did not compile. If the code had compiled, the async block would have captured a reference to self.user_fetch that lasted long enough, but it didn't compile because tower::Service::call doesn't actually promise that self lasts that long.

As you practice Rust you will see this sort of pattern often, and learn to know what to expect just by looking at the function signatures.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.