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
}