Как выполнить асинхронное исследование каталога и его подкаталогов - PullRequest
2 голосов
/ 22 июня 2019

Мне нужно изучить каталог и все его подкаталоги. Я могу легко изучить каталог с помощью рекурсии синхронно:

use failure::Error;
use std::fs;
use std::path::Path;

fn main() -> Result<(), Error> {
    visit(Path::new("."))
}

fn visit(path: &Path) -> Result<(), Error> {
    for e in fs::read_dir(path)? {
        let e = e?;
        let path = e.path();
        if path.is_dir() {
            visit(&path)?;
        } else if path.is_file() {
            println!("File: {:?}", path);
        }
    }
    Ok(())
}

Но когда я пытаюсь сделать то же самое асинхронно, используя tokio_fs:

use failure::Error;
use futures::Future;
use std::path::PathBuf;
use tokio::fs;
use tokio::prelude::*;

fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
    let task = fs::read_dir(path)
        .flatten_stream()
        .for_each(|entry| {
            println!("{:?}", entry.path());
            let path = entry.path();
            if path.is_dir() {
                let task = visit(entry.path());
                tokio::spawn(task.map_err(drop));
            }
            future::ok(())
        })
        .map_err(Error::from);

    task
}

Я получаю следующую ошибку:

   Compiling playground v0.0.1 (/playground)
error[E0391]: cycle detected when processing `visit::{{opaque}}#0`
 --> src/main.rs:9:28
  |
9 | fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
note: ...which requires processing `visit`...
 --> src/main.rs:9:1
  |
9 | fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  = note: ...which requires evaluating trait selection obligation `futures::future::map_err::MapErr<impl futures::future::Future, fn(failure::error::Error) {std::mem::drop::<failure::error::Error>}>: std::marker::Send`...
  = note: ...which again requires processing `visit::{{opaque}}#0`, completing the cycle
note: cycle used when checking item types in top-level module
 --> src/main.rs:1:1
  |
1 | / use failure::Error;
2 | | use futures::Future;
3 | | use std::path::PathBuf;
4 | | use tokio::fs;
... |
23| |     task
24| | }
  | |_^

Ссылка на игровую площадку

Как правильно асинхронно исследовать каталог и его подкаталоги (распространяя все ошибки)?

1 Ответ

2 голосов
/ 22 июня 2019

В вашем коде есть две ошибки:

Во-первых, функция, возвращающая impl Trait, в настоящее время не может быть рекурсивной, поскольку фактический возвращаемый тип будет зависеть от самого себя.

Таким образом, чтобы ваш пример работалвам нужно вернуть размерный тип.Очевидным кандидатом является объект черты, то есть Box<Future<...>>:

fn visit(path: PathBuf) -> Box<Future<Item = (), Error = Error>> {
    ...
            let task = visit(entry.path());
            tokio::spawn(task.map_err(drop));
    ...

    Box::new(task)
}

Есть еще ваша вторая ошибка:

dyn futures::future::Future<...>` cannot be sent between threads safely
   |
17 |                 tokio::spawn(task.map_err(drop));
   |                 ^^^^^^^^^^^^ `dyn futures::future::Future<...>` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `dyn futures::future::Future<...>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn futures::future::Future<...>`
   = note: required because it appears within the type `std::boxed::Box<dyn futures::future::Future<...>`
   = note: required because it appears within the type `futures::future::map_err::MapErr<...>`
   = note: required by `tokio::executor::spawn`

Это означает, что ваш объект черты не Send, поэтому его нельзя запланировать на выполнение в другом потоке, используя tokio::spawn().К счастью, это легко исправить: просто добавьте + Send к вашему объекту черты:

fn visit(path: PathBuf) -> Box<Future<Item = (), Error = Error> + Send> {
    ...
}

См. Полный код на Playground .

...