Help returning a list of Paths from Walkdir

I'm trying to find all files with an specific extension with a function an return a Vec with the paths of all of them. This is my first project in rust and I think I'm stuck here.

This is my function:
I'm using anyhow for the Result<T>

fn find_file_with_extension_in_path(extension: &str, path: PathBuf) -> Result<Vec<&Path>> {
    let matching_files: Vec<&Path> = WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| has_extension(e, extension))
        .map(|x| x.path())
        .collect();
        
    Ok(matching_files)
}

This is the error I get:

error[E0515]: cannot return value referencing function parameter `x`
  --> src/main.rs:65:18
   |
65 |         .map(|x| x.path())
   |                  -^^^^^^^
   |                  |
   |                  returns a value referencing data owned by the current function
   |                  `x` is borrowed here

I don't know how to handle this, and if anyone can give me some pointers why this is happening I would be very glad. Again, I'm quite new to rust and struggling a bit.

The borrow issue goes away if I change:
let matching_files: Vec<&Path> = WalkDir::new(path)
to
let matching_files: Vec<Path> = WalkDir::new(path)
But if I change the return value type to Result<Vec<Path>> I get this error:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
   --> src/main.rs:60:72
    |
60  | fn find_file_with_extension_in_path(extension: &str, path: PathBuf) -> Result<Vec<Path>> {
    |                                                                        ^^^^^^^^^^^^^^^^^ borrow the `Path` instead
    | 
   ::: /home/merlin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/liballoc/vec.rs:300:16
    |
300 | pub struct Vec<T> {
    |                - required by this bound in `std::vec::Vec`
    |

Any help with this one or guidance about where to go from here would be appreciated and sorry if this is a bad question in advance.

You can take a ref x instead

let matching_files = Vec<&Path> = WalkDir::new(path)
    /*  ... */
    .map(|ref x| x.path())
    .collect();

Although in your case you will need a clone, since you can't return &Path because it's owner won't live long enough. So, this is more likely the code you need:

fn find_file_with_extension_in_path(extension: &str, path: &Path) -> Result<Vec<PathBuf>> {
    let matching_files: Vec<PathBuf> = WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| has_extension(e, extension))
        .map(|x| x.path().to_owned())
        .collect();
        
    Ok(matching_files)
}

Path, like an str is a DST, and must be behind a reference (or Box).

1 Like

Thanks for your answer, I should've read the types a bit more and I would realize I can't return a Path as it's the like a slice of a PathBuf.

Your suggestion worked.

I checked the ref keyword and got a bit confused by it. We just use it in closures? As in other scenarios we would use it just in the type like x: &DirEntry or something like that?

In this case, &DirEntry would work too.
ref is the opposite of & in a way. It's useful in closures, matches and even with variables.
Example with a match, this can be solved by matching on a &v too:

#[derive(Debug)]
struct MyStruct;
impl MyStruct {
    fn x(&self) -> bool { true }
}

#[derive(Debug)]
enum MyEnum {
    A(MyStruct),
    B,
}
fn main() {
    let v = MyEnum::A(MyStruct);
    
    match v {
        // without `ref`, `a` will move out of `v` and later `dbg!` will cause a compile error
        MyEnum::A(ref a) if a.x() => println!("x() = true"),
        MyEnum::A(_) => println!("x() = false"),
        MyEnum::B => println!("B"),
    }
    
    dbg!(&v);
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.