Weekly Rust Trivia is a problem-oriented series of articles that assist developers while learning Rust. Every article solves simple, everyday development tasks using the Rust standard library or leveraging popular and proven crates.

Question: How to get a vector (Vec<String>) containing all filenames of a particular directory in Rust?

We can use the fs and io modules from Rust standard library this trivia.

use std::{fs, io};

fn get_files_in_directory(path: &str) -> io::Result<Vec<String>> {
    // Get a list of all entries in the folder
    let entries = fs::read_dir(path)?;

    // Extract the filenames from the directory entries and store them in a vector
    let file_names: Vec<String> = entries
        .filter_map(|entry| {
            let path = entry.ok()?.path();
            if path.is_file() {
                path.file_name()?.to_str().map(|s| s.to_owned())
            } else {
                None
            }
        })
        .collect();

    Ok(file_names)
}

In this example, we pass the path of the desired directory as argument to the get_files_in_directory function. We then use the fs::read_dir() method to get a list of all entries in the directory, which returns a std::fs::ReadDir iterator. We use the filter_map() method to filter out any None values from the iterator, then check whether each entry is a file or a directory using the is_file() method of the std::path::Path struct.

If the entry is a file, we extract the filename using the file_name() method and convert it to a String using the to_str() and to_owned() methods. If the entry is a directory, we return None to filter it out of the iterator. Finally, we use the collect() method to collect the filenames into a Vec<String>.

We can call the get_files_in_directory function as shown here:

fn main() {
    match get_files_in_folder("/tmp") {
        Ok(file_names) => {
            for file_name in file_names {
                println!("{}", file_name);
            }
        }
        Err(e) => println!("Error: {}", e),
    }
}

Community Feedback

As suggested by Thomas Darimont here, it may be more convinient to return a Vector of std::path::PathBuf which allows further processing for every item. See the following implementation of get_files_in_folder returning a Vec<PathBuf>. Basically, It allows the callee to do things like:

  • check if PathBuf is a directory or a file
  • check if PathBuf is a symlink
  • do even more complex examination by consulting PathBuf::metadata()
use std::{fs, io, path::PathBuf};

fn get_files_in_folder(path: &str) -> io::Result<Vec<PathBuf>> {
    let entries = fs::read_dir(path)?;
    let all: Vec<PathBuf> = entries
        .filter_map(|entry| Some(entry.ok()?.path()))
        .collect();
    Ok(all)
}

The alternative implementation can be used like this:

fn main() {
    match get_files_in_folder("/tmp") {
        Ok(files) => {
            for file in files {
                if file.is_dir() {
                    println!("{} is a directory", file.display());
                    continue;
                } 
                if file.is_symlink() {
                    println!("{} is a symlink", file.display());
                    continue;
                }
                
                let Ok(m) = file.metadata() else {
                    println!("Could not get metadata for {}", file.display());
                    continue;
                };

                if m.len() == 0 {
                        println!("{} is an empty file", file.display());
                        continue;
                }
                println!("{} is a file", file.display());
            }
        }
        Err(e) => println!("Error: {}", e),
    }
}