0

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?

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
mensi
  • 8,850
  • 1
  • 29
  • 42

1 Answers1

2

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:

Shepmaster
  • 274,917
  • 47
  • 731
  • 969