Как вы называете future :: select для Future, хранящегося в структуре, из метода, который принимает & mut self? - PullRequest
0 голосов
/ 04 марта 2020

Я хочу сохранить будущее, которое мы иногда ожидаем, в структуре. Мой вариант использования - получить сигнал, чтобы мой обработчик сетевых пакетов корректно завершил работу. Минимальный пример может выглядеть следующим образом, с зависимостью от фьючерса 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())
}

Проблема в том, что select хочет переместить значение в версии fn wait(..), поэтому я получаю компиляцию ошибка:

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

Обходной путь, с которым я столкнулся, можно увидеть в fn wait_optional: я (ab) использую Option для хранения будущего, вынимаю его при необходимости и затем помещаю его назад как select возвращает неожиданное будущее в Either. Это компилируется и, кажется, работает просто отлично - но это кажется хакерским. Есть ли «правильный» способ добиться этого?

1 Ответ

1 голос
/ 04 марта 2020

Возьмите непостоянную ссылку на будущее:

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())
}

Это потому, что Future - это , реализованный для любой непостоянной ссылки на Future с определенными ограничениями:

impl<'_, F> Future for &'_ mut F
where
    F: Unpin + Future + ?Sized, 

См. Также:

...