rust - Vanilla Rust中的目录遍历

标签 rust

我是Rust的新手,试图了解基本的目录遍历。我发现的几乎所有示例都利用了walkdirglob库,这些库我都取得了很好的成功。但是,我现在正尝试仅使用std lib来执行此操作。
标准lib文档中有一个primitive example,其中列出了以下功能:

fn visit(path: &Path, cb: &dyn Fn(&PathBuf)) -> io::Result<()> {
    for e in read_dir(path)? {
        let e = e?;
        let path = e.path();
        if path.is_dir() {
            visit(&path, cb)?;
        } else if path.is_file() {
            cb(&path);
        }
    }
    Ok(())
}
我很困惑的部分是如何在闭包的上下文中访问cb函数。我很难找到一个例子。
例如,我想做一些基本的事情,例如将结果路径收集到Vec中。显然,这不起作用:
fn main() {
    // create a new path
    let path = Path::new(PATH);
    let mut files = Vec::new();

    visit(path, |e| {
      files.push(e);
    });
}
我收到的错误是:
expected reference `&dyn for<'r> std::ops::Fn(&'r std::path::PathBuf)`
     found closure `[closure@src/main.rs:24:17: 26:6 files:_]
所以我的问题是,如何返回Fn并在闭包上下文中处理结果?

最佳答案

您的代码有多个问题,但是第一个收到错误消息的原因是因为&dyn Fn(&PathBuf)期望引用一个函数。您可以按照错误消息中的建议解决该错误:help: consider borrowing here: '&|e| files.push(e)'这将使您的调用变成:

visit(path, &|e| files.push(e));
但是,此代码仍然不正确,并导致另一个错误:
error[E0596]: cannot borrow `files` as mutable, as it is a captured variable in a `Fn` closure
  --> playground\src\main.rs:48:22
   |
48 |     visit(path, &|e| files.push(e));
   |                      ^^^^^ cannot borrow as mutable
这次是因为您要在files(不可变闭包)中对Fn进行突变。要解决此问题,您需要将函数类型更改为FnMut(有关更多信息,请参见Closures As Input Parameters):
fn visit(path: &Path, cb: &dyn FnMut(&PathBuf))
但是您仍然没有完成。现在存在另一个错误,但与第一个错误一样,它带有有关需要更改的建议:
error[E0596]: cannot borrow `*cb` as mutable, as it is behind a `&` reference
  --> playground\src\main.rs:39:13
   |
32 | fn visit(path: &Path, cb: &dyn FnMut(&PathBuf)) -> io::Result<()> {
   |                           -------------------- help: consider changing this to be a mutable reference: `&mut dyn for<'r> std::ops::FnMut(&'r std::path::PathBuf)`
...
39 |             cb(&path);
   |             ^^ `cb` is a `&` reference, so the data it refers to cannot be borrowed as mutable
为了使闭包能够可变地借用它使用的数据,还必须对闭包本身进行可变引用,并且需要更新visit()调用以匹配:
fn visit(path: &Path, cb: &mut dyn FnMut(&PathBuf))
...
visit(path, &mut |e| files.push(e));
几乎在那里,但是有一个最终错误要解决:
error[E0521]: borrowed data escapes outside of closure
  --> playground\src\main.rs:48:26
   |
47 |     let mut files = Vec::new();
   |         --------- `files` declared here, outside of the closure body
48 |     visit(path, &mut |e| files.push(e));
   |                       -  ^^^^^^^^^^^^^ `e` escapes the closure body here
   |                       |
   |                       `e` is a reference that is only valid in the closure body
您已将闭包定义为对PathBuf(&PathBuf)进行引用,但是您正试图将这些引用插入闭包外部的Vec中,这将不起作用,因为一旦闭包,这些引用将无效超出范围。相反,您应该使用一个拥有的值-只需PathBuf即可。您还需要更新对闭包的用法,以传递PathBuf而不是引用:
fn visit(path: &Path, cb: &mut dyn FnMut(PathBuf))
...
cb(path);
终于成功了!这是完整程序现在的样子。请注意,您还应该unwrap()调用visit(),因为它返回了Result。我还添加了一个简单的for循环以打印出文件名。
use std::path::{Path, PathBuf};
use std::fs::*;
use std::io;

fn visit(path: &Path, cb: &mut dyn FnMut(PathBuf)) -> io::Result<()> {
    for e in read_dir(path)? {
        let e = e?;
        let path = e.path();
        if path.is_dir() {
            visit(&path, cb)?;
        } else if path.is_file() {
            cb(path);
        }
    }
    Ok(())
}

fn main() {
    let path = Path::new("./your/path/here");
    let mut files = Vec::new();
    visit(path, &mut |e| files.push(e)).unwrap();
    for file in files {
        println!("{:?}", file);
    }
}

关于rust - Vanilla Rust中的目录遍历,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63542762/

相关文章:

rust - pyo3 入门示例未在 MacOS 上构建

Java 的速度几乎是 Rust 的两倍?!?我错过了什么?

vector - 通过迭代而不是使用 .push() 逐一填充结构体元素的向量

rust - `cfg` 哪个总是真/假?

PHP FFI - 将数组 rom Rust 函数返回给 PHP

multithreading - 对字段成员的Arc引用

collections - Rust - 如何将数据添加到三重嵌套 HashMap

rust - Change selector in match when selector is a mutable reference

generics - 为什么 Split 类型只返回 &str 即使 Pattern 具有 &str 和 char 的实现?

rust - 需要类型注解——为什么关联类型被区别对待?