Async recursion

Hello,
I wanted to dive more into tokio and async programming in rust and tried to implement directory crawler using tokio's fs. Here is the code:

use std::{fs, io};
use std::fs::*;
use std::path::Path;
use std::future::Future;

async fn visit_dirs(dir: &Path) -> io::Result<()> {
    if dir.is_dir() {
        let mut entries = tokio::fs::read_dir(dir).await?;

        while let Some(entry) = entries.next_entry().await? {
            let path = entry.path();
            if path.is_dir() {
                visit_dirs(&path).await?;
                // tokio::spawn(visit_dirs(&path));
            } 
        }

    }

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
   
    visit_dirs(Path::new(".")).await?;
  
    Ok(())
}

The problem is that mentioned code gives me following error:

    error[E0733]: recursion in an `async fn` requires boxing
  --> src/main.rs:22:36
   |
22 | async fn visit_dirs(dir: &Path) -> io::Result<()> {
   |                                    ^^^^^^^^^^^^^^ recursive `async fn`
   |

Could tou please help me? How should I implement async recursion in rust?
Thanks

Boxing the future will allow recursion:

use std::fs::*;
use std::future::Future;
use std::path::Path;
use std::pin::Pin;
use std::{fs, io};

fn visit_dirs<'a>(dir: &'a Path) -> Pin<Box<dyn Future<Output = io::Result<()>> + 'a>> {
    Box::pin(async move {
        if dir.is_dir() {
            let mut entries = tokio::fs::read_dir(dir).await?;

            while let Some(entry) = entries.next_entry().await? {
                let path = entry.path();
                if path.is_dir() {
                    visit_dirs(&path).await?;
                    // tokio::spawn(visit_dirs(&path));
                }
            }
        }

        Ok(())
    })
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    visit_dirs(Path::new(".")).await?;

    Ok(())
}

The async book has an explanation about why this is required: https://rust-lang.github.io/async-book/07_workarounds/05_recursion.html

1 Like

Thanks! I saw it but still had a problem to use it in my case. I also found following crate https://docs.rs/async-recursion/0.3.1/async_recursion/ to solve my problem

2 Likes

FYI there's a handy type alias BoxFuture if you don't want to keep writing

Pin<Box<dyn Future<...> + 'a + Send>>

all the time.

2 Likes

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.