#async-await #await #async #implicit #proc-macro

macro no-std implicit-await-macro

#[implicit_await]过程宏实现

1个不稳定版本

0.1.0 2019年5月18日

#15 in #implicit


用于 implicit-await

CC0 协议

15KB
113 代码行,不包括注释

implicit-await - 隐式Future等待的Rust

用法

需要 nightly Rust编译器,直到async_await功能稳定。

将此添加到您的Cargo.toml

[dependencies]
implicit-await = "0.1"

现在您可以使用#[implicit_await]

use implicit_await::implicit_await;

#[implicit_await]
async fn foo() {
    // Any functions returning a future (`async fn` or `fn -> impl Future`) will be automatically awaited.
}

描述

Rust的异步/await功能使用后缀.await运算符来显式请求在async函数中的挂起点。关于.await运算符的语法讨论已经很长,并且存在争议,这是由于与Rust现有的后缀?运算符(以及其他一些关注点)的兼容性所驱动的。

当我阅读有关.await最终成为领导者的各种语法提案时,我突然想到,如果使async函数的包装/解包行为在内部保持一致,那么问题将大大简化。在标准Rust中,async函数隐式地将它们的返回值包装在Future中,但async函数的输入——确定编译器生成状态机形状的子Futures——是通过.await显式解包的。如果相反,async函数同时隐式处理包装和解包,那么就不再需要.await运算符,绕过了对运算符的语法关注。

此库提供了一个过程宏#[implicit_await],该宏将async函数转换为隐式解包子Future

标准Rust #[implicit_await]
输出(函数结果)是... 隐式包装 隐式包装
输入(子未来)是... 显式解包(.await) 隐式解包

示例

有关以下内容的完整工作演示,请参阅sum示例。

假设你有以下三个函数,从完全同步到完全异步。

type IntResult = Result<u32, ()>;

// Fully synchronous
fn num_sync(num: u32) -> IntResult {
    Ok(num)
}

// Half-and-half
fn num_fut(num: u32) -> impl Future<Output = IntResult> {
    ready(Ok(num))
}

// Fully asynchronous
async fn num_async(num: u32) -> IntResult {
    Ok(num)
}

在标准Rust中,同步和异步函数的调用通过是否需要后缀.await运算符来区分。

async fn sum() -> IntResult {
    // No .await - a synchronous function.
    let one: u32 = num_sync(1)?;
    // .await - asynchronous function.
    let two: u32 = num_fut(2).await?;
    // .await - asynchronous function.
    let three: u32 = num_async(3).await?;
    // Note that the return value is implicitly wrapped.
    Ok(one + two + three)
}

implicit-await中,由于隐式等待行为,同步和异步函数的行为等效。这使得?运算符能够按预期工作,而无需任何额外语法。

#[implicit_await]
async fn sum() -> IntResult {
    // Synchronous function.
    let one: u32 = num_sync(1)?;
    // A `fn -> impl Future` is implicitly awaited.
    let two: u32 = num_fut(2)?;
    // An `async fn` is implicitly awaited.
    let three: u32 = num_async(3)?;
    Ok(one + two + three)
}

implicit-await使得异步函数的调用在程序控制流方面与同步函数的调用行为相同。直观的规则“函数在返回之前完成”,这是Rust程序员从调用同步函数中自然学到的,也适用于异步函数调用。然而,有时你会有需要显式控制挂起点的用例。`defer!`宏允许你`defer`等待,以便你可以并行启动多个异步进程。这种用例的常见示例是同时进行多个网络请求,然后等待它们全部完成。

use implicit_await::defer;

#[implicit_await]
async fn sum() -> IntResult {
    // Start three futures in parallel without implicitly awaiting.
    let (one, two, three) = defer!{ (
        num_fut(1),
        num_async(2),
        num_async(3)
    ) };
    // This `join` call is implicitly awaited because the defer! block ended.
    let (one, two) = futures::future::join(one, two);
    // If you have a bare `Future`, you're free to .await it if you want.
    let three = three.await?;
    Ok(one? + two? + three);
}

有什么问题吗?

简而言之:error[E0599]: 没有在当前作用域中找到名为 `as_future` 的方法 for type `your::type::here`

问题是Rust不支持负的或相互排斥的特质界限。正因为如此,我需要做出选择,是痛苦地无缝支持Future类型,还是痛苦地无缝支持!Future类型和痛苦地支持Future类型。我选择了前者。这意味着不实现Future的类型需要手动支持。

你正在调用返回... 然后你需要...
来自std的类型 什么也不做("std"是默认功能)
来自第三方crate的类型 检查功能列表,以查看是否已经支持该crate的隐式await,如果是,则启用该功能。否则,提交一个请求支持该crate类型被添加的问题。摇滚明星可以包括一个PR来添加支持,并附带问题!
来自你自己的crate的类型 使用as_future!宏在你的代码中添加支持。或者,提交一个PR到implicit-await以支持你的crate,并在功能标志之后。

功能

  • std默认功能)- 启用对Rust标准库类型的支持。禁用此功能以支持no-std

待办事项

  • 修复as_future!宏中的错误,该错误导致多泛型调用无法正常工作。
  • 遍历整个std并添加类型
  • 想出一个需要添加支持的crate列表,并这样做。

依赖项

我尽量限制可能的依赖。项目 implicit-await 的直接依赖项有 proc-macro2quotesyn,它们都用于实现过程宏 #[implicit_await]unicode-xid 作为传递依赖被引入。

implicit-await v0.1.0
└── implicit-await-macro v0.1.0
    ├── proc-macro2 v0.4.30
    │   └── unicode-xid v0.1.0
    ├── quote v0.6.12
    │   └── proc-macro2 v0.4.30 (*)
    └── syn v0.15.34
        ├── proc-macro2 v0.4.30 (*)
        ├── quote v0.6.12 (*)
        └── unicode-xid v0.1.0 (*)

致谢

显然,Kotlin 中的 futures 的工作方式与此类似。

依赖项

~2MB
~46K SLoC