#closures #macro #async #traits #utilize #features #boxing

nightly async_closure

这个crate使用nightly-only功能async_fn_in_trait来模仿async_closures

3个版本

0.1.2 2023年3月30日
0.1.1 2023年3月3日
0.1.0 2023年3月1日

异步中排名732

每月下载量35

MIT/Apache

21KB
221

async_closure

GitHub issues github test suite crates.io docs.rs

这个crate利用nightly-only功能async_fn_in_trait来模仿async_closures

这次你不必在异步代码中将局部闭包返回的Future装箱!

使用这个crate的步骤

  1. 选择一个用于特质边界的异步特质

    • 当确定没有临时引用类型被捕获时(即所有捕获的类型满足'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
    }
    
  2. 使用其伴随宏来自动生成一个值,其类型是独特且未命名的,但实现了该特质。宏的语法包含两部分

    • 对于capture_lifetimes风格的宏

      1. 一个代码块{},其中声明了多个通过,分隔的赋值语句field_name: field_type = field_value
      2. 一个异步闭包async |arg1: arg1_type, ...| -> return_type { /* 任何异步代码 */ }

      注意:AsyncFn*系列只包含单个生命周期参数'a,并且你必须在field_typereturn_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_typereturn_type不能是临时引用类型
  3. 将值传递给调用函数

    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
常见问题解答
  1. Rust 的要求?

MSRV:v1.69.0,由于async_fn_in_trait特性,目前只在夜间版本中可用。

  1. 我为什么需要这个?

为了避免像之前所说的那样,将局部闭包返回的 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.
}
  1. 如何在稳定的 Rust 上工作?

目前不可能。请参阅上面的第二个问题,其中提供了链接,显示了传统的众所周知的稳定方法,特别是对于非捕获异步回调/函数。

  1. 还有更多示例吗?

是的。每个特性和宏都有其详细的用途。还可以查看来自 URLO 的示例文件夹,它解决了某些片段问题。

没有运行时依赖