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:
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:
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: