async_closure
This crate utilizes the nightly-only feature async_fn_in_trait
to imitate async_closures.
You don't have to Box the return Future from a local closure in async code this time!
The steps to use this crate:
-
Choose an async trait which is used in trait bounds
- use traits in [
capture_no_lifetimes
] mod when you're sure there won't be any temporarily referenced type to be captured (i.e. all captured types statisfy'static
bound). - otherwise, use traits in [
capture_lifetimes
] mod.
// 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` use ; // 1. Adjust your caller where the trait bound looks like the following async
- use traits in [
-
Use its companion macro to auto generate a value, the type of which is distinct and unnamed but implemented with the trait. The syntax in macros contains two parts:
-
For capture_lifetimes style macros:
- a block
{}
where multiple assignmentsfield_name: field_type = field_value
seperated by,
are declared - an async closure
async |arg1: arg1_type, ...| -> return_type { /* any async code here */ }
Note:
AsyncFn*
family only contains single lifetime parameter'a
, and you must use it infield_type
andreturn_type
to express the non-static referenced type. But if the type there doesn't contain a reference,'a
is needless. - a block
let = ; // 2. Define a capturing closure let cb1 = async_closure_mut!;
- For non capture_lifetimes style macros, same components except that
- you can't use
'a
- i.e.
field_type
andreturn_type
can't be temporary referenced types
- i.e.
- you can't use
-
-
Pass the value into the caller function
take_a_mut_closure.await.unwrap; // That's it :)
macro | trait | capture references | mutate fields | times to be used |
---|---|---|---|---|
[async_closure! ] |
[capture_lifetimes::AsyncFn ] |
√ | × | no limit |
[async_closure_mut! ] |
[capture_lifetimes::AsyncFnMut ] |
√ | √ | no limit |
[async_closure_once! ] |
[capture_lifetimes::AsyncFnOnce ] |
√ | √ | 1 |
[async_owned_closure! ] |
[capture_no_lifetimes::AsyncFn ] |
× | × | no limit |
[async_owned_closure_mut! ] |
[capture_no_lifetimes::AsyncFnMut ] |
× | √ | no limit |
[async_owned_closure_once! ] |
[capture_no_lifetimes::AsyncFnOnce ] |
× | √ | 1 |
- Requirement for Rust?
MSRV: v1.69.0, and nightly-only due to the async_fn_in_trait
feature.
- Why do I need this?
To avoid boxing the return Future from a local closure as I said.
Try this crate if you're not statisfied with the traditional approaches as discussed here. But they do work on stable Rust. If you're not familiar, it's worth reading.
If you can use async_fn_in_trait
feature, of course you probably define a custom trait with
meaningful method calls. But it also means to define context-based structs that are hardly used twice.
So this crate can generate these structs behind the scenes to reduce boilerplate code.
And an advantage over closures is you're able to keep the (non-once) structs alive as long as you want.
async
async
- How to work on stable Rust?
Impossible for now. See the second question above that gives a link to show traditional well-known stable ways, especially for non-capturing async callbacks/functions.
- More examples?
Yes. Each trait and macro has its own usecase in details. Also have a look at examples folder which comes from URLO as a solution to some snippet question.