我想創建一個通用函數,它獲取目錄的路徑并在其上爬行,將可選函數應用于在該目錄和每個子目錄中找到的每個文件。
我在兩個我不明白的案子上遇到了死胡同。
這是第一例。我只對一個目錄應用函數,但不知道如何鍵入我的None
案例:
fn main() {
fn file_fn (file: &str) {
println!("file: {:?}", file)
}
fn dir_fn (file: &str) {
println!("dir: {:?}", file)
}
// Passing case
apply_dir(".", Some(file_fn), Some(dir_fn));
// Failing case
apply_dir(".", Some(file_fn), None::<dyn Fn(&str)>);
// ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
}
fn apply_dir<F, G> (dir: &str, file_func: Option<F>, dir_func: Option<G>)
where F: Fn(&str), G: Fn(&str) {
for path in read_dir(dir).unwrap() {
if path.is_dir() {
match dir_func {
Some(ref func) => func(path.to_str().unwrap()),
_ => (),
}
} else {
match file_func {
Some(ref func) => func(path.to_str().unwrap()),
_ => (),
}
}
}
}
fn read_dir(dir: &str) -> io::Result<Vec<PathBuf>> {
let mut paths = fs::read_dir(dir)?
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, io::Error>>()?;
paths.sort();
Ok(paths)
}
我在類型中有dyn
,因為它無論如何都會自動執行,并且沒有聲明它有一天會被棄用,但我不知道它會做什么。
這是第二種情況。我正在嘗試對目錄進行爬網,并將該函數應用于子目錄中的每個文件。
fn main() {
fn file_fn (file: &str) {
println!("file: {:?}", file)
}
crawl_dir(".", Some(file_fn));
}
// --- same apply_dir and read_dir functions
fn crawl_dir<F> (dir: &str, file_func: Option<F>)
where F: Fn(&str) {
apply_dir(dir, file_func, Some(|f| crawl_dir(f, file_func)));
// --------- ^^^^^^^^^^^^^^^^^---------^
// | | |
// | | closure is `FnOnce` because it moves the variable `file_func` out of its environment
// | this closure implements `FnOnce`, not `Fn`
// the requirement to implement `Fn` derives from here
}
我基本上理解這個錯誤,但我不知道如何修復它。
file_func
的所有權被閉包占有,它被閉包消耗,使它成為FnOnce
。我找不到如何將閉包傳遞為Fn
,即使我沒有從它的作用域之外向它傳遞任何參數。
dyn
描述的是某個類型具有某些功能,bit沒有說明該類型的數據,因此沒有說明任何大小。dyn Fn(&str)
表示可以使用&str
參數調用傳入的類型,但這不足以確定需要為該類型分配多少內存。例如,兩者and
堅持
dyn func(&str)
,但b
占用更多空間,因為它還需要存儲對prefix
的引用。要解決第一個問題,可以傳入具有固定大小的類型,例如
fn(&str)
函數指針。至于第二個問題,它說的是因為關閉是
file_func
的所有權在這個閉包被調用之后,
file_func
將不再能夠被使用,但是您需要多次調用這個閉包(每個文件一次),因此需要多次使用file_func
。因此,file_func
的所有權需要遠離關閉。如果改為使用引用,則閉包將不具有所有權,并且可以多次調用它。