1个不稳定版本
0.1.0 | 2019年5月18日 |
---|
#15 in #implicit
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-macro2
、quote
和 syn
,它们都用于实现过程宏 #[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