Как реализовать `Futures :: poll` над внутренним методом `async fn func (mut self)`, который принимает полное владение?

#asynchronous #rust #future #tokio

#асинхронный #Ржавчина #будущее #tokio

Вопрос:

Как реализовать Futures::poll следующий код, который будет вызывать async метод с полным правом собственности на self ?

 use anyhow::Error;
use futures::Future;
use futures::channel::oneshot;
use futures::task::{Context, Poll};
use std::pin::Pin;

struct MyLongTask {
    rx: oneshot::Receiver<()>,
}

impl MyLongTask {
    // The method and full ownership to `self` is important to keep!
    async fn recv(mut self) -> Result<(), Error> {
        self.rx.await.map_err(|_| Error::msg("can't recv"))
    }
}

// TryFuture not necessary here
impl Future for MyLongTask {
    type Output = Result<(), Error>;

    fn poll(self: Pin<amp;mut Self>, cx: amp;mut Context<'_>) -> Poll<Self::Output> {
        todo!("how to `self.recv().await` here?")
    }
}

fn main() { }
 

Игровая площадка, если это необходимо.

Комментарии:

1. В чем преимущество создания MyLongTask future вместо прямого вызова rx.await.map_err(...) ? Какова цель?

2. Это упрощенный пример структуры со многими полями, которые необходимо использовать.

3. Если MyLongTask имеет много полей и нуждается в использовании и обработке recv , зачем создавать MyLongTask себе будущее? my_long_task.recv() уже возвращает future, который делает то, что вы хотите, нет?

Ответ №1:

Вы не можете вызвать self.recv() inside poll во-первых, потому что он не принадлежит self , а во-вторых, потому что это не async так. Future::poll является синхронным, но должен быстро возвращаться независимо от того, готово ли возвращаемое значение (в этом вся идея Poll::Pending ). В вашем случае вам следует просто делегировать poll to self.rx : (игровая площадка)

 impl Future for MyLongTask {
    type Output = Result<(), Error>;
    
    fn poll(mut self: Pin<amp;mut Self>, cx: amp;mut Context<'_>) -> Poll<Self::Output> {
        match Pin::new(amp;mut self.rx).poll(cx) {
            Poll::Ready(x) => Poll::Ready(x.map_err(|_| Error::msg("can't recv"))),
            Poll::Pending => Poll::Pending,
        }
    }
}
 

Теперь вместо использования task.recv().await вы можете просто сделать task.await .
Я бы также посоветовал вам либо реализовать, Future либо предоставить recv метод.
В противном случае вы можете столкнуться с проблемами позже, когда измените одну реализацию и забудете изменить другую.

Комментарии:

1. «а во-вторых, потому что это не async так » — чтобы быть педантичным, вы можете вызвать async функцию из не- async одного, вы просто не можете .await этого. Возвращенное будущее просто ничего не сделает, если что-то не продвинет его вперед.

2. Это не совсем то, что мне было нужно, но тоже полезно.