Is there a way to do recursive async calls on Rust without Box?

Recursive calls with a retry count embedded into the argument is good for the mental model of the project because you don't have to keep thinking about the state of the object. The retry_count is passed in every call. Here's a simple implementation:

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

fn do_or_fail() -> std::result::Result<(),()> {
    Ok(())
}

fn do_something<'a>(
        retry_count: u32
) -> BoxFuture<'a, std::result::Result<(), ()>> {
    async move {
        match do_or_fail() {
            Ok(_) => Ok(()),
            Err(_) => do_something(retry_count -1).await
        }
    }.boxed()
}

fn main() {
    do_something(3);
}

Playground

The problem is that this requires a dynamic allocation at every call so it can return the BoxFuture. This is because of how async is implemented in Rust. It generates a state machine for every await call so the return type is not Sized.

What would be good ways to overcome dynamic allocation in recursive async calls?

Ultimately, a recursive async function requires an allocation. In your case, how about rewriting it into a loop?

I was looking for a more functional approach so I could worry less about variables/state. Do you think I could do that in a loop? Maybe using closures inside the function so I call the closure over and over with the retry_count -1 value? Is it even possible to do a future closure?