14
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 + '_>> {    
    |                                                                                                              ^^^^

3 Answers 3

8

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;
    })
}
Sign up to request clarification or add additional context in comments.

5 Comments

I get 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::Body>, client::ClientActionError>>``
@Gatonito my apologies, I've updated the post to highlight that + Send is needed in the return type.
please look at my update. By requiring it to be Send, then I get problems with lifetime of self. I guess that now it knows that I might want to send this to other threads, so it can't ensure that self lives enough. Even though self is captured by the async, I don't use it on the return result. Isn't there a way to fix this?
@Gatonito Does my last paragraph and code sample not help in that case? If not, you'll probably have to provide more code in your question.
I'm confused in how your last conde helps. It takes off the &mut self, and what is the difference between Box::pin and .boxed()?
3

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

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.