Help with static lifetime on self when using async

As seen in the below example, I have two futures producing functions, wacky and works.
They both technically produce the same values, but wacky fails to compile due to warnings that I will paste below. Any assistance is appreciated.

#![feature(async_await)]

use std::future::Future;
use futures::future::{self, FutureExt};
use std::pin::Pin;
use std::task::{Context, Poll};

#[derive(Debug)]
struct Payload {
    item: u32
}

struct ResponseWrapper {
    inner: Pin<Box<dyn Future<Output=Payload> + Send>>
}

impl Future for ResponseWrapper {
    type Output = Payload;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        Future::poll(Pin::new(&mut self.inner), cx)
    }
}


trait Processor {
    fn process(&mut self, item: Payload) -> ResponseWrapper;
}


struct ConcreteProcessor;

impl Processor for ConcreteProcessor {
    fn process(&mut self, item: Payload) -> ResponseWrapper {
        let in_my_future = self.works(); // compiles
//        let in_my_future = self.wacky(); Fails to compile due to static lifetime associated with async
        ResponseWrapper { inner: in_my_future.boxed() }
    }
}

impl ConcreteProcessor {
    async fn wacky(&mut self) -> Payload {
        future::ready(Payload { item: 200 }).await
    }

    fn works(&mut self) -> impl Future<Output=Payload> {
        future::ready(Payload { item: 200 })
    }
}



#[tokio::main]
async fn main() {
    let mut processor = ConcreteProcessor;
    let result = processor.process(Payload { item: 340 }).await;

    println!("HUGE RES: {:?}", result);
}

cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src\main.rs:36:33
   |
36 |         let in_my_future = self.wacky();
   |                                 ^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 34:5...
  --> src\main.rs:34:5
   |
34 | /     fn process(&mut self, item: Payload) -> ResponseWrapper {
35 | | //        let in_my_future = self.works(); // compiles
36 | |         let in_my_future = self.wacky();
37 | |         ResponseWrapper { inner: in_my_future.boxed() }
38 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src\main.rs:36:28
   |
36 |         let in_my_future = self.wacky();
   |                            ^^^^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = Payload> + std::marker::Send + 'static)>>
              found std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = Payload> + std::marker::Send>>

TL,DR: The desugaring of an async fn function generates a + '_ explicitly elided output lifetime parameter, which is not the case of your method, which used an implicitly elided output lifetime parameter, giving + 'static.

You are comparing the two following methods:

It turns out that your works function has an elided lifetime in the return type (any impl Trait or dyn Trait have a + 'lifetime bound). In this case, the lifetime elision rules of trait objects / impl Trait lifetimes takes precedence over the lifetime elision rules of functions and methods, thus corresponding to:

fn works (self: &'_ mut Self) -> impl Future<Output = Payload> + 'static
{
    future::ready(Payload { item: 200 })
}

if you elide the lifetime parameter explicitely (instead of implicitly), like this:

fn wacky (self: &'_ mut Self) -> impl Future<Output = Payload> + '_
{
    future::ready(Payload { item: 200 })
}

then the method yields the same compilation error than your original wacky:

async fn wacky (self: &'_ mut Self) -> Payload
{
    future::ready(Payload { item: 200 }).await
}
1 Like

Thank you for the clarity regarding lifetime elision. Since there is no way to add the 'static lifetime to the Payload struct in fn wacky, it would seem that the only way to really use async/await in this method is by basically doing something like this:

    fn wacky(&mut self) -> impl Future<Output=Payload> {
        async {
            future::ready(Payload { item: 200 }).await
        }
    }

Disappointing, but it at least compiles.

Yes, that's the solution when you are not using one of the arguments of the function (in this case, self) within the Future / async part:

Unfortunately, I do not think that helps my case.

The issue that I am having is that I actually still need the reference to self inside my async block. In my actual real life example, my Processor has a Framed tokio::TcpClient, so I would like to do something like.

async move {
   let result = self.client.send("a request string").await.unwrap();
  Ok(result.data)
}

Oh, then either self.client can be Cloned, in which case you can do as shown above and pre-initialize the future with a let client = self.client.clone(); before the async move { /* uses client */ } block.

To easily get client to be Clone + Send, you can wrap it in an Arc:

struct ConcreteProcessor {
    client: Client,
}
// becomes
struct ConcreteProcessor {
    client: Arc<Client>,
}

(and if you need &mut access to Client, then there needs to be something like a RwLock in between...)


Otherwise you do have to be returning a '_-bounded future.

On the other hand, your ResponseWrapper struct can be made more flexible, to accept non-'static boxed futures:

struct ResponseWrapper<'inner> {
    inner: Pin<Box<dyn Future<Output=Payload> + Send + 'inner>> // instead of implicitly `static
}

impl Processor for ConcreteProcessor {
    fn process (self: &'_ mut Self, item: Payload) -> ResponseWrapper<'_>
    {
        let in_my_future = self.wacky(); // lifetime '_
        ResponseWrapper { inner: in_my_future.boxed() }
    }

Thanks for the explanation.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.