我是Rust的新手,试图了解基本的目录遍历。我发现的几乎所有示例都利用了walkdir
或glob
库,这些库我都取得了很好的成功。但是,我现在正尝试仅使用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/