首页 > 解决方案 > 如何使用异步回调编写异步递归 walkdir 函数

问题描述

我正在尝试编写一个异步函数,它将递归地遍历文件系统树,并为找到的每个文件调用一个异步回调。这是为了学习,我没有真正的用例。

这是我到目前为止所拥有的:

use async_std::{
    fs::{self, *},
    path::*,
    prelude::*,
}; // 1.5.0, features = ["unstable"]
use futures::{
    executor::block_on,
    future::{BoxFuture, FutureExt},
}; // 0.3.4
use std::{marker::Sync, pin::Pin};

fn main() {
    fn walkdir<F>(path: String, cb: &'static F) -> BoxFuture<'static, ()>
    where
        F: Fn(&DirEntry) -> BoxFuture<()> + Sync + Send,
    {
        async move {
            let mut entries = fs::read_dir(&path).await.unwrap();
            while let Some(path) = entries.next().await {
                let entry = path.unwrap();
                let path = entry.path().to_str().unwrap().to_string();
                if entry.path().is_file().await {
                    cb(&entry).await
                } else {
                    walkdir(path, cb).await
                }
            }
        }
        .boxed()
    }

    let foo = async {
        walkdir(".".to_string(), &|entry: &DirEntry| async {
            async_std::println!(">> {}\n", &entry.path().to_str().unwrap()).await
        })
        .await
    };

    block_on(foo);
}

我通过某种反复试验得到了这一步,但现在我被这个错误困在异步关闭回调上

warning: unused import: `path::*`
 --> src/main.rs:3:5
  |
3 |     path::*,
  |     ^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `pin::Pin`
  --> src/main.rs:10:25
   |
10 | use std::{marker::Sync, pin::Pin};
   |                         ^^^^^^^^

error[E0308]: mismatched types
  --> src/main.rs:33:54
   |
33 |           walkdir(".".to_string(), &|entry: &DirEntry| async {
   |  ______________________________________________________^
34 | |             async_std::println!(">> {}\n", &entry.path().to_str().unwrap()).await
35 | |         })
   | |_________^ expected struct `std::pin::Pin`, found opaque type
   |
   = note:   expected struct `std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = ()> + std::marker::Send>>`
           found opaque type `impl core::future::future::Future`

标签: rust

解决方案


use async_std::{
    fs::{self, *},
    path::*,
    prelude::*,
}; // 1.5.0
use futures::{future::{Future, FutureExt, LocalBoxFuture}, executor}; // 0.3.4

fn main() {
    async fn walkdir<R>(path: impl AsRef<Path>, mut cb: impl FnMut(DirEntry) -> R)
    where
        R: Future<Output = ()>,
    {
        fn walkdir_inner<'a, R>(path: &'a Path, cb: &'a mut dyn FnMut(DirEntry) -> R) -> LocalBoxFuture<'a, ()>
        where
            R: Future<Output = ()>,
        {
            async move {
                let mut entries = fs::read_dir(path).await.unwrap();

                while let Some(path) = entries.next().await {
                    let entry = path.unwrap();
                    let path = entry.path();
                    if path.is_file().await {
                        cb(entry).await
                    } else {
                        walkdir_inner(&path, cb).await
                    }
                }
            }.boxed_local()
        }

        walkdir_inner(path.as_ref(), &mut cb).await
    }

    executor::block_on({
        walkdir(".", |entry| async move {
            async_std::println!(">> {}", entry.path().display()).await
        })
    });
}

显着变化:

  • 接受AsRef<Path>而不是一个String和一个通用闭包而不是一个特征对象引用
  • 将闭包类型更改FnMut为更宽松
  • 闭包返回任何未来的类型。
  • 有一个内部实现函数隐藏了递归异步函数所需的丑陋 API。
  • 回调采用DirEntry值而不是引用。

也可以看看:


推荐阅读