In the following mockup, a struct containing a reqwest::Client
can have a method demo_good
returning a boxed future reqwest::Response
, without any lifetime constraint on the object owning the client. However, if I split this logic up into an async demo_post
method to make the actual post request to the client -- a placeholder for more complicated logic that will catch 401s and reauthenticate -- and demo_bad
that uses this helper method run a query an return a boxed future reqwest::Response
. Logically, the response does not use depend on data in the struct and so the demo_good
method compiles without issue. However, there is a compilation error for the demo_post
+ demo_bad
method, as the compiler flags the future returned by the first to be tied to the lifetime of self
:
use core::pin::Pin;
use reqwest::{Client, Error, Response, Url};
use std::collections::HashMap;
struct SessionManager {
client: Client,
}
impl SessionManager {
// POST to the execute_url, handling reauthentication
async fn demo_post(
&mut self,
url: Url,
body: HashMap<String, String>,
) -> Result<Response, Error> {
// Run first request
let response = self.client.post(url).json(&body).send().await;
// Todo: catch 401s, reauth, and repeat
dbg!(&response);
// Return
response
}
pub fn demo_good(
&mut self,
url: Url,
) -> Pin<Box<dyn futures::Future<Output = Result<Response, Error>> + Send>> {
let mut map = HashMap::new();
map.insert("fake_key".to_string(), "fake_value".to_string());
let response = self.client.post(url).json(&map).send();
Box::pin(response)
}
pub fn demo_bad(
&mut self,
url: Url,
) -> Pin<Box<dyn futures::Future<Output = Result<Response, Error>> + Send>> {
let mut map = HashMap::new();
map.insert("fake_key".to_string(), "fake_value".to_string());
let response = self.demo_post(url, map);
Box::pin(response)
}
}
The error message is:
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/lib.rs:41:9
|
35 | &mut self,
| - let's call the lifetime of this reference `'1`
...
41 | Box::pin(response)
| ^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
|
help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
|
37 | ) -> Pin<Box<dyn futures::Future<Output = Result<Response, Error>> + Send + '_>> {
| ++++
error: could not compile `playground` (lib) due to 1 previous error
I'm wary of blindling following the recommendation to add a '_
lifetime bound, because while that makes this playground demo compile I believe that in my actual use case it will just kick the problem further down the road. I have client code right now that uses the returned reqwest::Response
from the real version of demo_good
just fine, creating and using a bytes stream, but if I switch to the real version of demo_bad
with the '_
lifetime bound, there just ends up being a error[E0515]: cannot return value referencing temporary value
at some point.
So, if possible I'd like to make this work just by making changes in the SessionManager
struct and without making changes in lifetime bounds of the returned responses. Is there a way to make this work?
(Posted originally at https://stackoverflow.com/questions/78697226/returning-and-using-an-reqwestresponse-from-an-async-method before I saw a link this this forum from the Rust Playground)