dcchut / async-recursion Goto Github PK
View Code? Open in Web Editor NEWProcedural macro for recursive async functions
License: Apache License 2.0
Procedural macro for recursive async functions
License: Apache License 2.0
This would be convenient for crates which re-export the async_recursion macro, like frameworks whose users need an async_recursion-like macro to function (e.g. to store async functions and closures in a struct).
If the documentation on the macro were on the macro directly, the re-export in dependent crates would show that documentation as well.
#[async_recursion::async_recursion]
async fn meow(n : Option<&str>) -> () {
meow(n).await
}
expands to (cleaned):
#[must_use]
fn meow(
n: Option<&str>,
) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async move { meow(n).await })
}
Can this crate be made to use just alloc
crate instead of being std
dependant? That would help a lot of embedded developers given that the most recent version of rust allows async await in no_std
scenarios out of the box.
Hello,
I am wondering how to deal with this situation.
use async_recursion::async_recursion;
#[tokio::main]
async fn main() {
count_down(5, None).await;
}
#[async_recursion]
async fn count_down(num: u32, foo: Option<&str>) {
if num == 0 {
return;
}
println!("{}, {:?}", num, foo);
count_down(num - 1, foo);
}
Output:
error: lifetime may not live long enough
--> src/main.rs:8:1
|
8 | #[async_recursion]
| ^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
9 | async fn count_down(num: u32, foo: Option<&str>) {
| - let's call the lifetime of this reference `'1`
|
= note: this error originates in the attribute macro `async_recursion` (in Nightly builds, run with -Z macro-backtrace for more info)
help: to declare that the trait object captures data from argument `foo`, you can add an explicit `'_` lifetime bound
|
8 | #[async_recursion] + '_
| ++++
The "help" given by rustc is invalid.
I am trying to implement a declarative macro that, given a type, implements a recursive asynchronous function that takes that type as parameter. However, it seems unfeasible when the given type is a reference, since the type may not live as long as the 'async_recursion lifetime.
A minimal example of the declarative macros is something as following:
macro_rules! help_please {
($param:ty) => {
#[async_recursion]
async fn recursive_fn<F>(param: $param, f: &F)
where
F: Fn($param) + Sync + Send,
{
f(param);
}
};
}
And then, if I use it like in the example down below, it does not compile and tells me to add explicit lifetime 'async_recursion to the type of 'param': &'async_recursion usize
. What cannot be done since the lifetime `async_recursion does not exist at that level.
macros_async::help_please!(&usize);
I am aware that my approach may be weird or absolutely wrong; however, I would like to know if there is any way to tell async_recursion which is the lifetime that should be taken as `async_recursion. Can this situation be resolved somehow? Thanks in advance!
While using the async_recursion
proc_macro the lifetime must be set for any generics. It would be nice if proc_macro could add the lifetime to all generics, so that IDE's don't complain about in-band
lifetimes.
Now I need to do
#[async_recursion::async_recursion]
fn foo<S: SomeTrait + Send + 'async_recursion>(trait: S) -> u64 {
...
}
Goal would be
#[async_recursion::async_recursion]
fn foo<S: SomeTrait + Send>(trait: S) -> u64 {
...
}
@dcchut I can try adding that.
For example, this expanded line of return type. I have a Box
type defined in the crate. Not sure if that is the reason the Box
here is not qualified.
::core::pin::Pin<Box<dyn ::core::future::Future<Output = Result<()>>>>
.
Hey there, I wanted to use this in my project, but I'm getting an error that I don't get when manually adjusting the function for recursion:
error[E0277]: `(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)` cannot be sent between threads safely
--> src/subsystems/crawler.rs:64:50
|
64 | let aid = ctx.system.spawn().with(
| ^^^^ `(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)>`
= note: required because it appears within the type `std::boxed::Box<(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)>`
= note: required because it appears within the type `std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)>>`
= note: required because of the requirements on the impl of `axiom::actors::Processor<subsystems::crawler::IndexCrawler<P>, std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)>>>` for `fn(subsystems::crawler::IndexCrawler<P>, axiom::actors::Context, axiom::message::Message) -> std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = std::result::Result<(subsystems::crawler::IndexCrawler<P>, axiom::actors::Status), std::boxed::Box<(dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static)>>> + 'static)>> {subsystems::crawler::IndexCrawler::<P>::index_crawler}`
I noticed a TODO in the code about a Send
option that is missing, is that related? Would it be simple enough to explain what must happen so I could make a PR?
I always use ? expressions.
However, after adding async, an inconvenient error occurs even if i use the method introduced in other documents or use your library.
This is a problem that occurs because the Pin and Box are additionally covered.
Do you know a neat solution to this?
Here is a playground link for demonstration, the function only executes once instead of the expected 10 times. The issue in the example is the missing await
on the recursive call.
Functions that are manipulated by this macro lose their #[must_use]
warning that usually comes with async
s. Manually adding the #[must_use]
to run
highlights the issue. Without the #[must_use]
, it is very easy for programmers to forget the await
and creates a confusing debugging experience with their code paths not executing.
Hey again @dcchut . Just found another case when a manual overwrite of the lifetime is required. I am having a function simillar to
pub async fn execute<T>(&self, executor: &'async_recursion E)
where
E: Executor + Send + Sync
{
*self.counter.lock().await -=1;
if self.counter.lock().await > 0 {
self.execute(executor).await;
}
}
Notice that I had to stipulate 'async_recursion
in it. If I remove it I get a wierd error, saying that 'life0
is doubly defined. Have you came accross this case before?
error[E0263]: lifetime name `'life0` declared twice in the same scope
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.