Reputation: 9826
I want to store a future that we occasionally await in a struct. My use-case is to have a signal to tell my network packet handler to shut down gracefully. A minimal example could look like this, with a dependency on futures 0.3:
use futures::{
executor::block_on,
future::{pending, select, Either, Future},
}; // 0.3.4
struct Foo<F: Future + Unpin> {
fut: F,
fut_opt: Option<F>,
}
impl<F: Future + Unpin> Foo<F> {
async fn wait(self: &mut Self) {
let bar = pending::<&str>();
match select(self.fut, bar).await {
Either::Left(_) => println!("foo"),
Either::Right(_) => println!("bar"),
}
}
async fn wait_optional(self: &mut Self) {
let bar = pending::<&str>();
if let Some(foo) = self.fut_opt.take() {
match select(foo, bar).await {
Either::Left(_) => println!("foo"),
Either::Right((_, foo_fut)) => {
self.fut_opt.replace(foo_fut);
println!("bar")
}
}
}
}
}
fn main() {
let mut foo = Foo {
fut: pending::<()>(),
fut_opt: Option::from(pending::<()>()),
};
block_on(foo.wait())
}
The problem is that select
wants to move the value in the fn wait(..)
version, so I'm getting a compile error:
error[E0507]: cannot move out of `self.fut` which is behind a mutable reference
--> src/main.rs:14:22
|
14 | match select(self.fut, bar).await {
| ^^^^^^^^ move occurs because `self.fut` has type `F`, which does not implement the `Copy` trait
A workaround I came up with can be seen in fn wait_optional
: I'm (ab)using an Option
to store the future, take it out when needed and then put it back in as select
returns the unawaited future in Either
. This compiles and seems to work just fine - but it feels hacky. Is there a "proper" way for achieving this?
Upvotes: 0
Views: 596
Reputation: 431489
Take a mutable reference to the future:
use futures::{
executor::block_on,
future::{pending, select, Either, Future},
}; // 0.3.4
struct Foo<F: Future + Unpin> {
fut: F,
}
impl<F: Future + Unpin> Foo<F> {
async fn wait(&mut self) {
let bar = pending::<&str>();
match select(&mut self.fut, bar).await {
Either::Left(_) => println!("foo"),
Either::Right(_) => println!("bar"),
}
}
}
fn main() {
let mut foo = Foo {
fut: pending::<()>(),
};
block_on(foo.wait())
}
This is because Future
is implemented for any mutable reference to a Future
with certain restrictions:
impl<'_, F> Future for &'_ mut F
where
F: Unpin + Future + ?Sized,
See also:
Upvotes: 2