Gatonito
Gatonito

Reputation: 1894

Can I use await inside async recursive functions?

pub async fn send_and_expect(&mut self, request: rtsp_types::Request<Body>, retrying: bool) -> std::result::Result<rtsp_types::Response<Body>, ClientActionError> {

I get:

recursion in an `async fn` requires boxing
    
recursive `async fn`
    
note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`rustc(E0733)

I found https://rust-lang.github.io/async-book/07_workarounds/04_recursion.html but it is for a function that does not use async.

What should be the way here?

I found Why recursive async functions require 'static parameters in Rust? and I changed my function to

pub fn send_and_expect(&mut self, request: rtsp_types::Request<Body>, retrying: bool) 
-> Pin<Box<dyn Future <Output = std::result::Result<rtsp_types::Response<Body>, ClientActionError>>>> {

but now I cannot use await inside my funtion. Also, how do I return things?

For example:

return Box::pin(Err(ClientActionError::CSeqMissing))

won't work

UPDATE:

Based on the answer, below, I get this on the recursion call:

194 |         }.boxed()
    |           ^^^^^ future created by async block is not `Send`
    |
    = help: the trait `std::marker::Send` is not implemented for `dyn futures::Future<Output = std::result::Result<rtsp_types::Response<Body>, ClientActionError>>`
note: future is not `Send` as it awaits another future which is not `Send`
   --> src/client.rs:170:36
    |
170 | ...                   return self.send_and_expect(request.clone(), true).await;
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `Pin<Box<dyn futures::Future<Output = std::result::Result<rtsp_types::Response<Body>, ClientActionError>>>>`, which is not `Send`

UPDATE 2:

error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
   --> src/client.rs:156:20
    |
154 |       pub fn send_and_expect(&mut self, request: rtsp_types::Request<Body>, retrying: bool) 
    |                              --------- this data with an anonymous lifetime `'_`...
155 |       -> Pin<Box<dyn Future<Output = std::result::Result<rtsp_types::Response<Body>, ClientActionError>>+ Send>> {    
156 |           async move {
    |  ____________________^
157 | |             let expected_cseq_header_value = rtsp_types::HeaderName::from_static_str("cseq").unwrap();
158 | |             let expected_cseq = request.header(&expected_cseq_header_value);
159 | |             let expected_cseq = match expected_cseq {
...   |
193 | |             Err(ClientActionError::Teardown)
194 | |         }.boxed()
    | |_________^ ...is captured here, requiring it to live as long as `'static`
    |
help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
    |
155 |     -> Pin<Box<dyn Future<Output = std::result::Result<rtsp_types::Response<Body>, ClientActionError>>+ Send + '_>> {    
    |                                                                                                              ^^^^

Upvotes: 14

Views: 6843

Answers (3)

zwlxt
zwlxt

Reputation: 375

Since Rust 1.77, this code now works:

async fn recursive_pinned() {
    Box::pin(recursive_pinned()).await;
    Box::pin(recursive_pinned()).await;
}

Reference: https://rust-lang.github.io/async-book/07_workarounds/04_recursion.html

Upvotes: 2

Blay
Blay

Reputation: 151

Also, in hopes that it'll help somebody else, there's a crate providing a macro to do the whole complicated function rewrite automatically: https://docs.rs/async-recursion/latest/async_recursion.

Upvotes: 0

kmdreko
kmdreko

Reputation: 60607

I found [...] but it is for a function that does not use async.

Yes it does. You should take a closer look at code. The original function is definitely async:

async fn recursive() {
    recursive().await;
    recursive().await;
}

... but now I cannot use await inside my function.

You can if you make an async {} block as the fix suggests:

use futures::future::{BoxFuture, FutureExt};

fn recursive() -> BoxFuture<'static, ()> {
    async move {
        recursive().await; // you can use await here
        recursive().await;
    }.boxed()
}

The idea is simply async return type needs to be boxed instead of an impl Future. So the fix is to create an async {} block, wherein you run your function as normal, and then box it in order to return it. This avoids the issue caused by nested async/await functions being monomorphised together.

So you should be able to:

pub fn send_and_expect(&mut self, request: rtsp_types::Request<Body>, retrying: bool) 
-> Pin<Box<dyn Future<Output = std::result::Result<rtsp_types::Response<Body>, ClientActionError>> + Send>> {
                                                                                                // ^^^^^^
    async move {
        // put your original code here
    }.boxed()
}

Also, how do I return things?

You can return things as normal, either via the last expression in the async block, or you can use return. You should simply the same as you would for a proper async function, don't worry about the Box::pin(...).

If you need the Future to satisfy any other trait bounds (Send, Sync, Unpin, etc.) then you can specify it along with the dyn Future<...>


The return type requires dyn Future<Output = ...> + Send to use .boxed().

If the contents of the async block cannot be made Send, you can do it manually like so (although most runtimes expect Futures to be Send so you'd have a hard time using it):

fn recursive() -> Pin<Box<dyn Future<Output = ()>>> {
    Box::pin(async move {
        recursive().await;
        recursive().await;
    })
}

Upvotes: 7

Related Questions