Get all file name without extension, borrow pb

Hi,

I am blocking of this problem of borrow for a moment now... Maybe you can see the problem to help me ? I tried to clone some value but each time, an error occurs in compilation...
I just wanted to have a function that can give the full path of a file without its extension in directories and subdirectories.
(I think file_stem is not the full path, but I can manage after with a path join...)

For example : 
rootDir
...dir1
......file.conf
......file.other
......dir1.2
..........file2.conf

I would like to get : 
rootDir/dir1/file
rootDir/dir1/dir1.2/file2
use std::ffi::OsStr;
use std::fs::{self, metadata};
use std::{
    io,
    path::{Path, PathBuf},
};

fn get_filename_without_extension(path: &PathBuf) -> Option<&str> {
    path.file_stem().and_then(OsStr::to_str)
}

fn get_all_file_name(vec: &mut Vec<&str>, path: &Path) -> io::Result<()> {
    eprintln!("CALL WITH {:#?}", path);
    let paths = fs::read_dir(&path)?;
    for path_result in paths {
        let full_path = path_result?.path();
        if metadata(&full_path)?.is_dir() {
            get_all_file_name(vec, &full_path)?
        } else {
            match full_path.extension() {
                Some(ext) if ext == "conf" => {
                    if let Some(name) = get_filename_without_extension(&full_path) {
                        vec.push(name.clone());
                    }
                }
                Some(e) => eprintln!("EXT {:#?}", e),
                _ => (),
            }
        }
    }

    for f in vec {
        eprintln!("### {:#?}", f);
    }
    Ok(())
}

fn main() {
    //io::Result<Vec<PathBuf>> {
    let mut vec = Vec::new();
    get_all_file_name(&mut vec, &PathBuf::from("."));
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0597]: `full_path` does not live long enough
  --> src/main.rs:22:72
   |
12 | fn get_all_file_name(vec: &mut Vec<&str>, path: &Path) -> io::Result<()> {
   |                                    - let's call the lifetime of this reference `'1`
...
16 |         let full_path = path_result?.path();
   |             --------- binding `full_path` declared here
...
22 |                     if let Some(name) = get_filename_without_extension(&full_path) {
   |                                                                        ^^^^^^^^^^ borrowed value does not live long enough
23 |                         vec.push(name.clone());
   |                         ---------------------- argument requires that `full_path` is borrowed for `'1`
...
30 |     }
   |     - `full_path` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to 1 previous error

1 Like

You might consider using PathBuf::set_extension, which removes the extension when called with an empty string.

1 Like

Thank you, you save my day.

I'm a newbie as well, but reading the code, it seems to me that the problem being highlighted is that you are pushing Vec<&str>, and then you do: vec.push(name.clone()); but what's being cloned there is the reference, right? The reference points to full_path which seems to be dropped (maybe at each iteration).

If full_path is dropped or does not live long enough, the references in the Vec point to freed memory, and it's unsafe code (although you don't use them there).

An alternative is to push copies of the strings themselves, so they do not depend on another variable for the lifetime.

I did this below, but it may make no sense, anyways, I hope the previous paragraph does help at all.

2 Likes

Good point !
Thank you very much for your time.

1 Like

The playground does tell this, I'm unsure whether the original code did:

call to `.clone()` on a reference 
in this situation does nothing
the type `str` does not implement `Clone`, 
so calling `clone` on `&str` copies the reference, 
which does not do anything and can be removed
1 Like