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

7

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;
    })
}
5
  • 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
    Commented Apr 10, 2021 at 5:05
  • @Gatonito my apologies, I've updated the post to highlight that + Send is needed in the return type.
    – kmdreko
    Commented Apr 10, 2021 at 6:18
  • 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
    Commented Apr 10, 2021 at 17:47
  • @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.
    – kmdreko
    Commented Apr 10, 2021 at 17:53
  • 1
    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()?
    – Gatonito
    Commented Apr 10, 2021 at 17:54
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

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.