Question
How can I list all files in a directory in Rust?
I am looking for the Rust equivalent of the following Python code:
import os
copyfiles = os.listdir('./')
Short Answer
By the end of this page, you will understand how to read directory contents in Rust using std::fs::read_dir, how to extract file names and paths, how to handle errors safely, and how to filter results when you only want files rather than directories.
Concept
In Rust, listing the contents of a directory is usually done with std::fs::read_dir.
read_dir returns an iterator over the entries inside a directory. Each entry is represented by a DirEntry, which can give you information such as:
- the full path
- the file name
- metadata about the entry
This matters because many real programs need to inspect folders:
- command-line tools that scan files
- backup scripts
- media organizers
- build systems
- log processors
Unlike Python's os.listdir, Rust makes error handling explicit. That means:
- opening the directory can fail
- reading a specific entry can fail
- converting a file name to a UTF-8 string may fail in some cases
Rust asks you to deal with those possibilities clearly, which leads to safer code.
Mental Model
Think of a directory like a box of labeled items.
read_dir("./")opens the box- the iterator lets you pull items out one by one
- each
DirEntryis one item label - you can inspect that label to get the name or full path
So instead of getting one finished list immediately, Rust often gives you a stream of entries that you process step by step.
Syntax and Examples
The basic syntax is:
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
let entry = entry?;
println!("{}", entry.path().display());
}
Ok(())
}
What this does
fs::read_dir("./")?opens the current directory- it returns an iterator of results
- each
entrymust also be unwrapped with? entry.path()gets the full path.display()formats the path for printing
Getting only file names
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
let entry = entry?;
let file_name = entry.file_name();
println!("{}", file_name.to_string_lossy());
}
Ok(())
}
Here:
file_name()returns anOsStringto_string_lossy()converts it into printable text safely
Collecting names into a vector
use std::fs;
fn main() -> std::io::Result<()> {
let mut files = Vec::new();
for entry in fs::read_dir("./")? {
let entry = entry?;
files.push(entry.file_name().to_string_lossy().into_owned());
}
println!("{:?}", files);
Ok(())
}
This is closer to Python's os.listdir, because it builds a list of names.
Step by Step Execution
Consider this example:
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
let entry = entry?;
println!("{}", entry.file_name().to_string_lossy());
}
Ok(())
}
Step by step:
-
fs::read_dir("./")?- Rust tries to open the current directory.
- If it fails,
mainreturns the error immediately.
-
for entry in ...- Rust starts looping over the directory entries.
- Each item is a
Result<DirEntry, std::io::Error>.
-
let entry = entry?;- If reading this specific entry failed, return the error.
- Otherwise, extract the
DirEntry.
-
entry.file_name()- Get the file or directory name only.
-
.to_string_lossy()- Convert the OS-specific string into printable text.
-
println!(...)- Print the name.
-
Ok(())- If everything succeeds, return success.
If the directory contained:
main.rsCargo.tomlsrc
then the output might be:
main.rs
Cargo.toml
src
Real World Use Cases
Directory listing is used in many practical situations:
- CLI tools: scan a folder and process every file
- Backup utilities: gather files before copying them elsewhere
- Image or video apps: load media files from a directory
- Log analysis: read all log files in a folder
- Static site generators: find content files to build pages
- Build tools: inspect source directories for compilation inputs
Example: only process .txt files in a folder.
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|ext| ext.to_str()) == Some("txt") {
println!("Text file: {}", path.display());
}
}
Ok(())
}
This pattern is common in scripts and automation tools.
Real Codebase Usage
In real Rust codebases, developers usually do more than just print entries.
Common patterns include:
Guard clauses
Return early if the directory cannot be read:
use std::fs;
fn list_dir(path: &str) -> std::io::Result<()> {
let entries = fs::read_dir(path)?;
for entry in entries {
let entry = entry?;
println!("{}", entry.path().display());
}
Ok(())
}
Filtering only files
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
let entry = entry?;
let path = entry.path();
if path.is_file() {
println!("File: {}", path.display());
}
}
Ok(())
}
Validation before processing
Developers often check:
- does the path exist?
- is it a directory?
- is the file extension expected?
Mapping into collections
use std::fs;
fn main() -> std::io::Result<()> {
let names: Vec<String> = fs::read_dir("./")?
.filter_map(|entry| entry.ok())
.map(|entry| entry.file_name().to_string_lossy().into_owned())
.collect();
println!("{:?}", names);
Ok(())
}
This style is useful when you want a final collection instead of printing inside a loop.
Error-aware processing
In production code, you may log errors for bad entries instead of stopping the whole operation.
Common Mistakes
1. Forgetting that read_dir returns results
Broken code:
use std::fs;
fn main() {
for entry in fs::read_dir("./") {
println!("{:?}", entry);
}
}
Problem:
fs::read_dir("./")returns aResult<ReadDir, Error>, not an iterator directly.
Correct version:
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
println!("{:?}", entry?);
}
Ok(())
}
2. Assuming every name is valid UTF-8
Broken code:
let name = entry.file_name().into_string().unwrap();
Problem:
- not all file names are valid UTF-8 on every system
unwrap()can panic
Safer version:
let name = entry.file_name().to_string_lossy().into_owned();
3. Expecting only files
read_dir returns all entries:
- files
- directories
- sometimes symlinks
If you only want files:
if entry.path().is_file() {
// process file
}
4. Ignoring per-entry errors
Even if opening the directory succeeds, a specific entry can still fail to read.
Correct approach:
for entry in fs::read_dir("./")? {
let entry = entry?;
println!("{}", entry.path().display());
}
5. Confusing file name and full path
file_name()gives just the last part, likenotes.txtpath()gives the whole path, like./notes.txt
Use the one that matches your needs.
Comparisons
| Concept | Rust | Python | Notes |
|---|---|---|---|
| List directory contents | fs::read_dir("./") | os.listdir("./") | Rust returns an iterator of results instead of a plain list |
| File name only | entry.file_name() | names returned directly | Rust uses OS-specific string types |
| Full path | entry.path() | often built with os.path.join(...) | Rust can give the full path directly |
| Error handling | Result, ? | exceptions | Rust makes failure explicit |
file_name() vs path()
| Method | Returns | Example |
|---|---|---|
file_name() | just the name | "main.rs" |
path() | full path | "./main.rs" |
Looping vs collecting
| Style | Best for |
|---|---|
for loop | simple printing or processing |
iterator chain with map and collect | building a Vec or transforming results |
Cheat Sheet
use std::fs;
fn main() -> std::io::Result<()> {
for entry in fs::read_dir("./")? {
let entry = entry?;
println!("{}", entry.path().display());
}
Ok(())
}
Quick rules
- Use
std::fs::read_dir(path)to read a directory. - It returns
Result<ReadDir, std::io::Error>. - Each item inside the iterator is also a
Result<DirEntry, std::io::Error>. - Use
entry.path()for full path. - Use
entry.file_name()for just the file name. - Use
to_string_lossy()for safe string conversion. - Use
path.is_file()to keep only files. - Use
path.is_dir()to keep only directories.
Common patterns
Get file names:
let name = entry.file_name().to_string_lossy().into_owned();
Get full path:
let path = entry.path();
Only files:
if entry.path().is_file() {
// process file
}
Collect into vector:
let items: Vec<String> = fs::read_dir("./")?
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().into_owned())
.collect();
FAQ
How do I list files in the current directory in Rust?
Use std::fs::read_dir("./") and loop through the returned entries.
What is the Rust equivalent of Python os.listdir()?
The closest equivalent is std::fs::read_dir(). Rust returns an iterator of directory entries rather than a ready-made list of strings.
How do I get only file names and not full paths?
Use entry.file_name() instead of entry.path().
How do I list only regular files in Rust?
Check entry.path().is_file() before processing the entry.
Why does Rust use OsString for file names?
Because file names are not always valid UTF-8 on all operating systems. OsString can represent OS-native file names safely.
Can read_dir fail even after opening the directory?
Yes. Opening the directory may succeed, but reading a specific entry can still fail, which is why each entry is also a Result.
How do I collect directory contents into a vector?
Use a loop with push, or use iterator methods like map and collect.
Does read_dir return files in sorted order?
You should not rely on that. If you need sorted results, collect them first and sort them yourself.
Mini Project
Description
Build a small Rust program that scans a directory and prints only regular files. This demonstrates how to read directory entries, filter out folders, and collect file names into a vector for later use. This kind of task appears in backup scripts, media tools, and command-line utilities.
Goal
Create a Rust program that lists only the files in the current directory and stores their names in a vector.
Requirements
Requirement 1
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.