How can I delete all the files in a directory but not the directory itself in Rust?

Viewed 3161

How can you delete all the files in a directory without deleting the directory itself?

I thought this would work but it gives an error:

for entry in std::fs::read_dir("tmp/my_items") {
    std::fs::remove_file(entry);
}
the trait `AsRef<std::path::Path>` is not implemented for `ReadDir`
3 Answers

If the directory isn't being used, watched, or anything else that prevents it being deleted. Then you could just delete the whole directory using fs::remove_dir_all() and create it immediately after using fs::create_dir(), i.e.:

use std::fs;

let path = "tmp/my_items";
fs::remove_dir_all(path).unwrap();
fs::create_dir(path).unwrap();

Otherwise, you seem to have confused what fs::read_dir() actually returns.

It returns a Result<ReadDir>, which need to be unwrapped. However, while ReadDir does implement Iterator, it doesn't actually produce paths. It actually produces Result<DirEntry>, which also need to be unwrapped and still isn't a path. To get the actual path you have to call path().

use std::fs;
use std::io;
use std::path::Path;

fn remove_dir_contents<P: AsRef<Path>>(path: P) -> io::Result<()> {
    for entry in fs::read_dir(path)? {
        fs::remove_file(entry?.path())?;
    }
    Ok(())
}

fn main() {
    remove_dir_contents("tmp/my_items").unwrap();
}

If the directory doesn't only strictly contain files, then you need to check the file type and remove directories and their contents as well.

use std::fs;
use std::io;
use std::path::Path;

fn remove_dir_contents<P: AsRef<Path>>(path: P) -> io::Result<()> {
    for entry in fs::read_dir(path)? {
        let entry = entry?;
        let path = entry.path();

        if entry.file_type()?.is_dir() {
            remove_dir_contents(&path)?;
            fs::remove_dir(path)?;
        } else {
            fs::remove_file(path)?;
        }
    }
    Ok(())
}

read_dir returns an iterator over io::Result<DirEntry> objects. DirEntry is not a path itself (it's got many separate features, and they didn't implement silent coercion to Path possibly to avoid confusion), but it has a .path() method. Something like this (based on the example given for .path()) should work:

for entry in std::fs::read_dir("tmp/my_items")? {
    let entry = entry?;
    std::fs::remove_file(entry.path())?;
}

As another answer has pointed out, std::fs::read_dir returns ReadDir (inside a Result), which is an iterator over DirEntrys (inside Results).

In a similar style to this answer, you could just remove the directory and all its contents and then recreate the directory in one go with Result::and_then.

use std::fs;

let path = "tmp/my_items";
fs::remove_dir_all(path).and_then(|_| fs::create_dir(path))?;
Related