#await #async-await #async #implicit

no-std implicit-await

隐式等待返回Future实现类型的调用

1个不稳定版本

0.1.0 2019年5月18日

异步 中排名第1824

CC0 许可证

13KB
78

implicit-await - 隐式为Rust中的Future等待

用法

需要 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的async/await特性使用后缀操作符 .await 来显式请求在 async 函数中的挂起点。关于 .await 操作符语法的讨论已经很久了,并且颇具争议,这主要是因为希望与Rust现有的后缀操作符(以及其他一些问题)无缝交互。

当我阅读关于 .await 最终成为领导者的一系列语法提案时,我意识到,通过使 async 函数的包装/解包行为内部一致,可以大大简化问题。在标准Rust中,async 函数隐式地将它们的返回值包装在 Future 中,但 async 函数的 输入 - 决定编译器生成的状态机形状的子 Futures - 通过 .await 显式解包。如果相反,async 函数隐式处理包装和解包,那么就无需 .await 操作符,绕过关于操作符的语法问题。

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

标准Rust #[implicit_await]
输出(函数结果)是... 隐式包装 隐式包装
输入(子futures)是... 显式解包 (.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!允许你延迟等待,以便可以并行启动多个异步过程。这种用例的常见示例是同时发出多个网络请求,然后等待它们全部完成。

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 类型 `your::type::here`

问题在于Rust不支持负的或互斥的特质约束。因此,我需要在支持Future类型无缝和!Future类型痛苦之间做出选择,或者在!Future类型无缝和支持Future类型痛苦之间做出选择。我选择了前者。这意味着不实现Future的类型需要手动支持。

你在调用同步函数返回... 然后你需要...
来自std的类型 什么都不做(“std”是默认特性)
来自第三方crate的类型 查看功能列表,以查看是否已经支持了该crate,如果是,则启用该功能。否则,提交一个请求支持该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