Как написать асинхронную рекурсивную функцию walkdir с асинхронным обратным вызовом - PullRequest
2 голосов
/ 17 февраля 2020

Я пытаюсь написать асинхронную c функцию, которая будет рекурсивно проходить по дереву файловой системы и вызывать асинхронный обратный вызов для каждого найденного файла. Это для обучения, у меня нет реального варианта использования.

Вот то, что у меня есть до сих пор:

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);
}

Я получаю это далеко методом проб и ошибок, но теперь я застрял на асин c закрытие обратного вызова с этой ошибкой

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`

1 Ответ

1 голос
/ 18 февраля 2020
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 и универсальное c закрытие вместо ссылки на объект признака
  • Изменить тип замыкания должен быть FnMut, поскольку он более разрешительный
  • Замыкание возвращает любой тип, являющийся будущим.
  • Существует внутренняя функция реализации, которая скрывает уродливый API, необходимый для рекурсивной асинхронизации c функции.
  • Обратный вызов принимает DirEntry по значению, а не по ссылке.

См. также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...