3个版本
0.1.2 | 2023年3月30日 |
---|---|
0.1.1 | 2023年3月3日 |
0.1.0 | 2023年3月1日 |
在异步中排名732
每月下载量35次
21KB
221 行
async_closure
这个crate利用nightly-only功能async_fn_in_trait
来模仿async_closures。
这次你不必在异步代码中将局部闭包返回的Future装箱!
使用这个crate的步骤
-
选择一个用于特质边界的异步特质
- 当确定没有临时引用类型被捕获时(即所有捕获的类型满足
'static
约束),在capture_no_lifetimes
模块中使用特质。 - 否则,在
capture_lifetimes
模块中使用特质。
// e.g. take a closure that potentially captures references and will change its states. // 0. import the `AsyncFnMut` trait and companion macro `async_closure_mut` #![feature(async_fn_in_trait)] #![allow(incomplete_features)] use async_closure::{capture_lifetimes::AsyncFnMut, async_closure_mut}; // 1. Adjust your caller where the trait bound looks like the following async fn take_a_mut_closure<'env, T, F>(mut cb: F) -> T where // 'env means the lifetime of captured variables outside the function // 'any means the lifetime of arguments coming from inside the function // also note how we express and represent one argument via `(arg1, )`, // likewise we can declare more arguments via `(arg1, arg2, arg3, )` etc. F: for<'any> AsyncFnMut<'env, (&'any str,), Output = T>, { let mut s = String::from("-"); let args = (&s[..],); // type inference doesn't work well here cb.call_mut(args).await; s.push('+'); let args = (&s[..],); cb.call_mut(args).await }
- 当确定没有临时引用类型被捕获时(即所有捕获的类型满足
-
使用其伴随宏来自动生成一个值,其类型是独特且未命名的,但实现了该特质。宏的语法包含两部分
-
对于capture_lifetimes风格的宏
- 一个代码块
{}
,其中声明了多个通过,
分隔的赋值语句field_name: field_type = field_value
- 一个异步闭包
async |arg1: arg1_type, ...| -> return_type { /* 任何异步代码 */ }
注意:
AsyncFn*
系列只包含单个生命周期参数'a
,并且你必须在field_type
和return_type
中使用它来表示非静态引用类型。但如果那里的类型不包含引用,'a
就不再需要。 - 一个代码块
let (outer, mut buf, buffer) = (String::new(), String::new(), String::new()); // 2. Define a capturing closure let cb1 = async_closure_mut!({ s: &'a str = &outer, // shared reference buf: &'a mut String = &mut buf, // mutable reference arc: Arc<str> = buffer.into(), // owned type without explicit mutation len: usize = 0, // owned type with mutation, see the code below }; async |arg: &str| -> Result<usize, ()> // Annotate both inputs and output: no lifetime parameter on arguments' type! { // Write async code here, using the field names and argument names as variables. // Note: the type of fields here depends on `AsyncFn*`. // i.e. for this macro, all field comes from `let Self { pattern_match } = &mut self;` // thus `s: &mut &'a str`, `buf: &mut &'a mut String` etc. // If you use `async_closure!`, then `s: &&'a str`, `buf: &&'a mut String` etc. // If you use `async_closure_once!`, then `s: &'a str`, `buf: &'a mut String` etc. Ok(*len) });
- 对于非 capture_lifetimes 风格的宏,组件相同,除了
- 你不能使用
'a
- 即
field_type
和return_type
不能是临时引用类型
- 即
- 你不能使用
-
-
将值传递给调用函数
take_a_mut_closure(cb1).await.unwrap(); // That's it :)
宏 | 特质 | 捕获引用 | 修改字段 | 使用次数 |
---|---|---|---|---|
async_closure! |
capture_lifetimes::AsyncFn |
√ | × | 无限制 |
async_closure_mut! |
capture_lifetimes::AsyncFnMut |
√ | √ | 无限制 |
async_closure_once! |
capture_lifetimes::AsyncFnOnce |
√ | √ | 1 |
async_owned_closure! |
capture_no_lifetimes::AsyncFn |
× | × | 无限制 |
async_owned_closure_mut! |
capture_no_lifetimes::AsyncFnMut |
× | √ | 无限制 |
async_owned_closure_once! |
capture_no_lifetimes::AsyncFnOnce |
× | √ | 1 |
常见问题解答
- Rust 的要求?
MSRV:v1.69.0,由于async_fn_in_trait
特性,目前只在夜间版本中可用。
- 我为什么需要这个?
为了避免像之前所说的那样,将局部闭包返回的 Future 框起来。
如果你对传统方法不满意,可以尝试这个 crate(如这里讨论的那样)。但它们在稳定的 Rust 中确实有效。如果你不熟悉,值得阅读。
如果你可以使用async_fn_in_trait
特性,当然你可能定义一个具有有意义的调用方法的自定义特质。但这也意味着要定义基于上下文的、很少被重复使用的结构体。
所以这个 crate可以在幕后生成这些结构体,以减少样板代码。
并且与闭包相比的优势是,你能够根据需要保持(非一次性的)结构体的生命周期。
async fn take_and_return_a_mut_closure<'env, T, F>(mut cb: F) -> (T, F)
where
F: for<'any> AsyncFnMut<'env, (&'any str,), Output = T>,
{
let s = String::from("-");
(cb.call_mut((&s[..],)).await, cb) // Note: return the closure type
}
async fn test4() {
let mut buf = String::new();
let cb = async_closure_mut!({
buf: &'a mut String = &mut buf
}; async |arg: &str| -> () {
buf.push_str(arg);
});
let (_output, cb_again) = take_and_return_a_mut_closure(cb).await;
cb_again.buf.push('+'); // Still use it
assert_eq!(cb_again.buf, "-+");
take_a_mut_closure(cb_again).await; // And pass it into another function
// Note: since AsyncFnMut is the subtrait to AsyncFnOnce,
// you can pass it into a fucntion that requires AsyncFnOnce
// as long as they have identical generic parameters.
}
- 如何在稳定的 Rust 上工作?
目前不可能。请参阅上面的第二个问题,其中提供了链接,显示了传统的众所周知的稳定方法,特别是对于非捕获异步回调/函数。
- 还有更多示例吗?
是的。每个特性和宏都有其详细的用途。还可以查看来自 URLO 的示例文件夹,它解决了某些片段问题。