假设我有一个异步函数 foo
返回一些值。每次调用后返回的值都是不同的(例如,可以是从文件中读取数据块或随机数生成器)。
我想实现一个围绕 foo
实现 AsyncRead
. 包装器从 foo
,以某种方式处理它们,然后将它们放入用户提供的缓冲区。
这是我试过的。
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,
}
}
}
这显然不能编译,因为 poll()
想要一个寄托的价值。现在。我如何将未来归来的 foo
? 我试过各种组合 Box::pin
, Pin::new
和 pin_mut!
但都没有用。
还有 我应该存储的是 Future
在我的结构中 FooReader
直到它准备好了? 还是说我每次打完电话后,即使是待处理,也可以自由放弃?
EDIT: 下面的工作。
let mut future: Pin<Box<dyn Future<Output = u8>>> = Box::pin(foo());
match future.as_mut().poll(ctx) {
...
}
出于某种原因,我必须给 future
的类型注解,否则就不能编译(不知为何,编译器会混淆了 impl Future
和 dyn Future
).
即使可行,我还是想知道这是否是 "官方 "的做法。
由于某些原因,在调用函数foo后,返回的 Future
特质不在标准的前奏中,所以你必须要有 use
(提示就在那里,埋在所有其他编译器错误之间)。
use std::future::Future;
那么,是的,你必须在使用前锁定未来。在你的例子中,最简单的方法是用 pin_mut!
,它使用了一个有趣的语法。
let f = foo();
pin_mut!(f);
现在,你可以调用 poll()
但请记住,你必须转发你的 Context
争论。
match f.poll(ctx) {
游乐场 与工作代码。
关于存储未来或放弃未来,你可以做任何你想做的事情,这取决于具体的语义。我个人希望你能保留未来,并将其运行到完成,除非文档中有其他明确的规定。
当然,如果你把未来存储在你的struct.C中,那么你就可以把未来存储在你的struct.C中。pin_mut!
将无法工作,因为它消耗了它的参数。你需要把它放在一个 Pin<Box>
或 计划 销子 self
,因为它已经被钉住了。
为了保持 Pin<Box>
你做的事情一样。
pub struct FooReader {
f: Option<Pin<Box<dyn Future<Output=u8>>>>,
}
然后使用它不要忘记 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) {
你可能要检查 self.f
呼叫前 foo()
再次,但你懂的。
的选择。凸针 是比较好的,因为你避免了额外的分配给 盒子但它更复杂,需要额外的箱子或一点不安全的代码。如果你真的有兴趣,我认为它值得单独提出一个新问题。