I've encountered an obstacle when trying to implement Future.
I need to poll another async function in my poll function which only takes in &str.
It looks something like this:
async fn make_query(query: &str) -> i32 {
// Magic happens
5
}
As this is a function from another library I cannot simply change its signature to take ownership of the values.
Now my struct Executor, for which I want to implement Future, holds a String which should be referenced when calling make_query:
struct Executor {
query: String,
future: Mutex<Option<Pin<Box<dyn Future<Output = i32>>>>>
}
impl Future for Executor {
type Output = i32;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut lock = self.future.lock().unwrap();
if let None = *lock {
// This is where the error occurs
*lock = Some(Box::pin(make_query(&self.query)))
}
let future = lock.as_mut().unwrap();
pin!(future).poll(cx)
}
}
Now, the purpose of this is that I can .await my Executor to automatically make the query:
#[tokio::main]
async fn main() {
let result = Executor {
query: "foo".to_string(),
future: Mutex::new(None)
}.await;
println!("Result is {result}");
}
Now, apparently the Mutex requires that every reference passed to it is 'static? I got these error messages:
error: lifetime may not live long enough
--> src/main.rs:23:26
|
16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
| - let's call the lifetime of this reference `'1`
...
23 | *lock = Some(Box::pin(make_query(&self.query)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
error[E0597]: `self` does not live long enough
--> src/main.rs:23:47
|
16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
| ---- binding `self` declared here
...
23 | *lock = Some(Box::pin(make_query(&self.query)))
| ---------------------^^^^--------
| | |
| | borrowed value does not live long enough
| cast requires that `self` is borrowed for `'static`
...
29 | }
| - `self` dropped here while still borrowed
How do I tell Rust that Executor is only dropped when it has been completely polled? I can't change the function signature of poll as that would violate the trait specifications.
Or, more likely, what is the correct way to do what I want to do?
Here's a link to the playground.
PS: I am aware that calling .await to execute the query might be considered an 'anti pattern'.
Solution
Checkout drewtato's answer for an explanation.
The solution (for me) was to remove the future field from my struct:
struct Executor {
query: String
}
and then, instead of implementing Future, implement IntoFuture which gets implicitly called when using .await. Here we can use async move {} which moves the value instead of just referencing it.
Note that, at this point, type IntoFuture = impl Future is unstable and I thus had to use Pin<Box<dyn Future>>
impl IntoFuture for Executor {
type Output = i32;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
make_query(&self.query).await
})
}
}
Now you can call .await on Executor:
#[tokio::main]
async fn main() {
let res = Executor {
query: "foo".into()
}.await;
println!("Result is {res}"); // 5
}