简体   繁体   English

在异步 Rust 中,如何实现轮询的包装器?

[英]In async Rust, how to I implement a wrapper around poll?

Suppose I have an async function foo that returns some value.假设我有一个异步 function foo返回一些值。 The value returned is different after each call (this could be eg something that reads chunks of data from a file or a random number generator).每次调用后返回的值都不同(例如,这可能是从文件或随机数生成器中读取数据块的东西)。

I want to implement a wrapper struct around foo that implements AsyncRead .我想在foo周围实现一个实现AsyncRead的包装器结构。 The wrapper reads the values from foo , processes them in some way, and then places them into the user-supplied buffer.包装器从foo读取值,以某种方式处理它们,然后将它们放入用户提供的缓冲区中。

Here's what I've tried:这是我尝试过的:

use futures::io::AsyncRead;
use pin_utils::pin_mut;
use std::io;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;

async fn foo() -> u8 {
    b'x'
}

pub struct FooReader;

impl AsyncRead for FooReader {
    fn poll_read(self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8])
        -> Poll<io::Result<usize>> 
    {
        if buf.is_empty() {
            return Poll::Ready(Ok(0));
        }
        match foo().poll() {  // << problem here
            Poll::Ready(byte) => {
                buf[0] = byte;
                Poll::Ready(Ok(1))
            },
            Poll::Pending => Poll::Pending,
        }
    }
}

(Link to Playground) (链接到游乐场)

This obviously doesn't compile because poll() wants a pinned value.这显然不能编译,因为poll()想要一个固定值。 Now, how do I pin the future returned by foo ?现在,我如何固定foo返回的未来? I tried various combinations of Box::pin , Pin::new and pin_mut!我尝试了Box::pinPin::newpin_mut! but none of them worked.但他们都没有工作。

Also, am I supposed to store the Future in my struct FooReader until it's ready?另外,我是否应该将Future存储在我的 struct FooReader中,直到它准备好? Or am I free to drop it after each call, even if it's pending?或者我是否可以在每次通话后随意放弃它,即使它正在等待处理?


EDIT: The following works:编辑:以下作品:

let mut future: Pin<Box<dyn Future<Output = u8>>> = Box::pin(foo());
match future.as_mut().poll(ctx) {
    ...
}

(Link to Playground) (链接到游乐场)

For some reason I have to give future an explicit type annotation, otherwise it doesn't compile (somehow the compiler is making confusion between impl Future and dyn Future ).出于某种原因,我必须给future一个明确的类型注释,否则它不会编译(编译器不知何故混淆了impl Futuredyn Future )。

Even if it works, I would still like to know if this is the "official" way to do it.即使它有效,我仍然想知道这是否是“官方”的方式。

For some reason, the Future trait is not in the standard prelude, so you have to use it before calling the function (the hint is there, buried between all the other compiler errors):出于某种原因, Future特征不在标准前奏中,因此您必须在调用 function 之前use它(提示就在那里,隐藏在所有其他编译器错误之间):

use std::future::Future;

Then, yes, you must pin the future before use.那么,是的,您必须在使用前固定未来。 The easiest way in your case is with pin_mut!在您的情况下,最简单的方法是使用pin_mut! , that uses a funny syntax: ,它使用了一个有趣的语法:

let f = foo();
pin_mut!(f);

Now, you can call poll() , but remember that you must forward your Context argument:现在,您可以调用poll() ,但请记住您必须转发Context参数:

match f.poll(ctx) {

Playground with working code.带有工作代码的游乐场

About storing the future or dropping it, you can do whatever you want, that depends on the exact semantics.关于存储未来或丢弃它,你可以做任何你想做的事情,这取决于确切的语义。 I personally would expect you to keep the future and run it to completion unless there is something in the documentation that explicitly says otherwise.我个人希望您保留未来并将其运行到完成,除非文档中有明确说明的内容。

Naturally, if you store the future in your struct, pin_mut!当然,如果你将未来存储在你的结构中, pin_mut! will not work because it consumes its argument.将不起作用,因为它消耗了它的论点。 You need to either keep it in a Pin<Box> , or project the pin from self , since it is already pinned.您需要将其保存在Pin<Box>中,或者从self投射引脚,因为它已经被固定了。

To keep the Pin<Box> you do something like:要保留Pin<Box> ,您可以执行以下操作:

pub struct FooReader {
    f: Option<Pin<Box<dyn Future<Output=u8>>>>,
}

And then to use it do not forget the Box::as_mut() since you need a Pin<&mut Future> , not a Pin<Box<impl Future>> :然后使用它不要忘记Box::as_mut() ,因为您需要Pin<&mut Future> ,而不是Pin<Box<impl Future>>

        let f = foo();
        self.f = Some(Box::pin(f)); //remember to declare (mut self) above!
        match self.f.as_mut().unwrap().as_mut().poll(ctx) {

You'll probably want to check self.f before calling foo() again, but you get the idea.您可能想在再次调用foo()之前检查self.f ,但您明白了。

The option of the pin projection is nicer because you avoid the extra allocation for the Box , but it is more complex and requires extra crates or a bit of unsafe code. pin 投影的选项更好,因为您避免了Box的额外分配,但它更复杂并且需要额外的 crate 或一些不安全的代码。 If you are really interested I think it deserves a new question on its own.如果您真的感兴趣,我认为它本身值得提出一个新问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何围绕 C++ 中的异步回调创建同步包装器? - How can I create a synchronous wrapper around an async callback in C++? 有没有办法在 rust 异步中同时轮询多个期货 - Is there a way to poll several futures simultaniously in rust async 如何通过超时停止等待TaskCompletionSource周围的异步包装? - How to stop waiting for async wrapper around TaskCompletionSource by timeout? 同步代码的异步包装器中的 Rust 生命周期 - Rust lifetimes in async wrapper for sync code 如何通过具有完全所有权的内部`async fn func(mut self)`方法实现`Futures::poll`? - How to implement `Futures::poll` over internal `async fn func(mut self)` method that takes full ownership? 如何使Swift中围绕Alamofire.authenticate方法的登录异步函数包装器? - how to make login async function wrapper around Alamofire.authenticate method in swift? 如何创建用于处理异步数据传输的包装器? - How can I create a wrapper for handling Async data transfer? 如何为TcpListener编写自己的异步包装? - How do I write my own async wrapper for TcpListener? 如何围绕异步方法编写包装器? - How to write a wrapper around an asynchronous method? 如何实现异步INotifyPropertyChanged - How to implement async INotifyPropertyChanged
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM