There are multiple issues with your code, but the first one that you are getting the error message for is because &dyn Fn(&PathBuf) expects a reference to a function. You can resolve that error by following the suggestion from the error message: help: consider borrowing here: '&|e| files.push(e)'
That turns your call into:
visit(path, &|e| files.push(e));
However, this code is still incorrect and results in yet another error:
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
This time, it's because you're mutating files inside a Fn (immutable closure). To fix that, you need to change your function type to FnMut (see Closures As Input Parameters for more information):
fn visit(path: &Path, cb: &dyn FnMut(&PathBuf))
But you're still not done. There is now another error, but like the first, it comes with a suggestion for what needs to be changed:
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
In order for your closure to mutably borrow the data it uses, you also have to take a mutable reference to the closure itself, and you'll need to update your visit() call to match:
fn visit(path: &Path, cb: &mut dyn FnMut(&PathBuf)) ... visit(path, &mut |e| files.push(e));
Almost there, but there is one final error to resolve:
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
You've defined your closure to take a reference to a PathBuf (&PathBuf), but you're trying to push those references into a Vec that is outside of the closure, which won't work because those references will be invalid once the closure goes out of scope. Instead, you should use an owned value -- simply PathBuf. You'll also need to update your usage of the closure to pass the PathBuf instead of a reference:
fn visit(path: &Path, cb: &mut dyn FnMut(PathBuf)) ... cb(path);
It finally works! Here is what the full program looks like now. Note that you should also unwrap() your call to visit() since it returns a Result. I've also added a simple for loop to print out the file names.
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); } }
help: consider borrowing here: '&|e| files.push(e)'. If you fix that, you will see that you have further errors that need resolving.