从`async fn`返回的future的具体类型是啥?

Posted

技术标签:

【中文标题】从`async fn`返回的future的具体类型是啥?【英文标题】:What is the concrete type of a future returned from `async fn`?从`async fn`返回的future的具体类型是什么? 【发布时间】:2020-03-04 19:54:39 【问题描述】:

存储期货的向量应该使用什么类型?

我尝试在同一个 URL 上发出多个并发请求,并将所有未来保存到向量中以与 join_all 一起使用。

如果我没有明确地为向量设置类型,那么一切正常。我知道 Rust 可以找到正确的变量类型。 CLion确定向量类型为Vec<dyn Future<Output = ()>>,但是当我尝试自己设置类型时,却报错:

error[E0277]: the size for values of type `dyn core::future::future::Future<Output = ()>` cannot be known at compilation time
  --> src/lib.rs:15:23
   |
15 |     let mut requests: Vec<dyn Future<Output = ()>> = Vec::new();
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `dyn core::future::future::Future<Output = ()>`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: required by `std::vec::Vec`

我必须将类型声明为Vec&lt;Pin&lt;Box&lt;dyn Future&lt;Output=()&gt;&gt;&gt;&gt;,这迫使我将函数结果包装到requests.push(Pin::from(Box::new(request(i))));

use futures::future::join_all;
use std::future::Future;
use std::pin::Pin;

async fn request(n: i32) 
    println!("Started: ", n);
    let response = reqwest::get("https://www.rust-lang.org")
        .unwrap()
        .text()
        .unwrap();
    println!("Completed: . Response: ", n, &response[0..10]);


async fn main() 
    let mut requests: Vec<dyn Future<Output = ()>> = Vec::new();
    for i in 0..5 
        requests.push(request(i));
    
    join_all(requests).await;

应该是哪种类型?

【问题讨论】:

Vec&lt;dyn Future&lt;Output=()&gt;&gt; 不是有效类型,因为它的大小在编译时是未知的。它是表示从async fn request(n: i32) 返回的不透明类型的类型提示。如果它显示为Vec&lt;impl Future&lt;Output=()&gt;&gt;,这可能是一个更好的选择,但这仍然不是一个有效的类型。将此类型视为实现Future&lt;Output=()&gt;T。此外,除非您的不透明类型不同,否则您不需要 Pin Box 您的期货:请see 所以如果你仍然想明确声明类型,你至少可以做到let mut requests:Vec&lt;_&gt; = Vec::new(); 我还是不明白,如果我没有显式声明一个值类型,如何让 Rust 识别它?为什么我写不出来? "async/.await 是 Rust 的内置工具,用于编写看起来像同步代码的异步函数。async 将代码块转换为状态机,该状态机实现了一个名为未来"(请参阅:async await primer) 抱歉,我看了三遍以上,还是没看懂。 Rust 将异步块转换为状态机,但它的状态机围绕某个结构工作,它的结构可以是某种类型,可以设置为变量。或者它只是一种生锈的魔法,我不在乎这个,因为我不能用它做一些?我能做的最好的事情就是让 Rust 处理变量类型。 【参考方案1】:

来自RFC:

异步函数的返回类型是编译器生成的唯一匿名类型,类似于闭包的类型。你可以把这种类型想象成一个枚举,函数的每个“屈服点”都有一个变体——它的开头、等待表达式和每个返回。每个变体都存储了从该屈服点恢复控制所需存储的状态。

当函数被调用时,这个匿名类型在它的返回 初始状态,其中包含此函数的所有参数。

您不能显式声明未来的具体类型,因为它是匿名类型。作为 API 用户,我们只需要知道它实现了std::futures::Future,但这并不意味着我们不需要对这种匿名类型及其实现有更深入的了解,掌握这个概念会很好。


CLion 判断向量类型为Vec&lt;dyn Future&lt;Output = ()&gt;&gt;

这是一个类型提示,不是实际类型,因为编译器无法知道dyn Future&lt;Output = ()&gt;的大小,所以不会编译。


Pin&lt;Box&lt;_&gt;&gt;-ing Future 来声明显式类型可能不是一个好主意。在您的情况下,它不需要,因为从 async fn 返回的具体类型是相同的。让编译器推断类型就好了。

另见:

对于各种具体的返回类型: How can I put an async function into a map in Rust? 静态和动态调度:Trait Objects

【讨论】:

你能举例说明如何在没有Pin&lt;Box&lt;_&gt;&gt;的情况下声明具体类型吗? @Austaras 正如我在答案中指出的那样;从async fn 返回的Future 是一个匿名类型,它是由编译器生成的,你不能声明,因为你不知道编译器会生成什么。你在处理什么样的情况,如果你给我一个例子,我可以试着帮忙?

以上是关于从`async fn`返回的future的具体类型是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现对`async fn(&mut self)`进行轮询的`Future`?

为啥 std::future 从 std::packaged_task 和 std::async 返回不同?

c++中的异步编程——future,promise

flutter async和await原理解析

Python asyncio ensure_future 装饰器

如何从使用 Futures/async/await 的流中正确生成?