Run async fn conditionally inside recursive async fn

I have three async functions, all of them returning a Result<T, MyError>. The first one is called recursively, and looks like this:

#[async_recursion]
pub async my_recursive_async_fn() -> Result<(), MyError> {
  ...
}

Inside of it, I call two other async functions, of whom the first one returns a Result<Vec<SomeType>, MyError> and the second one is the one I would run to run conditionally based on the result being returned.

The following code does not compile because of type mismatch between the if...else arms:

my_async_fn()
.then(|result_from_my_async_fn| {
  if let Ok(some_vec) = result_from_my_async_fn {
    async move {
      my_other_async_fn(&some_vec).await
    }
  } else {
    ???
  }
})
.await
   

So far, what I have tried is:

  • Use an async block inside of the else clause to match the type returned by my_other_async_fn, which doesn't work because of the uniqueness of the types being returned by async blocks.

  • Don't return the async block, but a future that resolves immediately: futures::future::ok::<(), MyError>(()), which doesn't work because the resulting type (futures::future::Then<impl futures::Future, futures::future::Ready<Result<(), MyError>>) is not Send, which is a requirement from the async_recursion macro.

my_async_fn()
.then(|result_from_my_async_fn| {
  if let Ok(some_vec) = result_from_my_async_fn {
    async move {
      my_other_async_fn(&some_vec).await
    };

    futures::future::ok::<(), MyError>(())
  } else {
    futures::future::ok::<(), MyError>(())
  }
})
.await

I'm puzzled by this. Why is that the resulting future is not send? Is there anything in the picture that I'm missing here?

For the record, here's the full error being returned:

error: future cannot be sent between threads safely
   --> src/services/users/user_items_service.rs:687:5
    |
687 |     #[async_recursion]
    |     ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
    |
    = help: the trait `std::marker::Send` is not implemented for `(dyn StdError + 'static)`
note: future is not `Send` as this value is used across an await

my_async_fn()
.then(|result_from_my_async_fn| {
  if let Ok(some_vec) = result_from_my_async_fn {
    ...
})
.await;
    | |__________________^ first, await occurs here, with the value maybe used later...
note: the value is later dropped here
  .await;
    |                   ^
note: this has type `futures::future::Then<impl futures::Future, futures::future::Ready<Result<(), MyError>>, [closure@src/services/users/user_items_service.rs:691:19: 705:14]>` which is not `Send`
   --> src/services/users/user_items_service.rs:689:9
    |
my_async_fn()
.then(|result_from_my_async_fn| {
  if let Ok(some_vec) = result_from_my_async_fn {
    ...
})
    | |______________^
    = note: required for the cast to the object type `dyn futures::Future<Output = Result<(), MyError>> + std::marker::Send`

I know that the important part is this one: help: the trait `std::marker::Send` is not implemented for `(dyn StdError + 'static)` ; what I don't get is where does this type come from :confused:

What's the return type of the my_async_fn()?

What is MyError? If it's a type alias for Box<dyn std::error::Error + 'static> then it's not Send as the dynamic dispatch type doesn't require that bound and you may want to use Box<dyn std::error::Error + Send + Sync + 'static> instead.

1 Like

It's the one that returns Result<Vec<SomeType, MyError> :slight_smile: .

It's an Enum of struct errors... and you're right. It didn't go through my mind that I was actually boxing errors in some of my struct errors.

Thanks for the help, Hyeonu and xfix!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.