Introdução: por que você pode precisar de uma lista de arquivos no Rust?
\n\nImagine: você está escrevendo no Rust um utilitário para backup, um analisador de logs ou um simples gerenciador de arquivos. E surge uma tarefa básica, mas crítica — obter a lista de todos os arquivos em um diretório especificado. Parece simples, não? Mas no Rust, com sua tipagem forte e sistema de ownership, até mesmo uma operação tão simples esconde algumas armadilhas.
\n\nA pergunta «How can I list files of a directory in Rust?» é uma das mais populares no Stack Overflow (milhares de visualizações!). E não é por acaso: iniciantes frequentemente tropeçam no std::fs::read_dir, se confundem com iteradores e esquecem do tratamento de erros. Neste tutorial, mostrarei duas abordagens funcionais: a simples (apenas arquivos em uma pasta) e a avançada (travessia recursiva de todos os subdiretórios). Vamos lá!
1. Preparação: criando um novo projeto
\n\nAntes de escrever código, vamos criar um novo projeto binário. Abra o terminal e execute:
\n\ncargo new list_files_rs\ncd list_files_rs\n\nAgora abra src/main.rs — é aqui que escreveremos todo o código. Para começar, não precisaremos de dependências externas, apenas da biblioteca padrão do Rust — std.
2. A maneira mais simples: std::fs::read_dir
\n\nA função std::fs::read_dir retorna um iterador sobre as entradas do diretório. Cada entrada é um DirEntry, que contém nome, caminho, tipo e metadados do arquivo. Vamos escrever um exemplo básico:
use std::fs;\nuse std::path::Path;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n let path = Path::new("."); // diretório atual\n\n // Lê o conteúdo da pasta\n let entries = fs::read_dir(path)?;\n\n for entry in entries {\n let entry = entry?; // desempacota o Result\n let path = entry.path();\n\n // Verifica se é um arquivo (não um diretório)\n if path.is_file() {\n println!("{}", path.display());\n }\n }\n\n Ok(())\n}\n\nO que está acontecendo aqui?
\n- \n
fs::read_dir(path)— abre o diretório e retorna umReadDir(iterador). \n - Percorremos cada entrada. Cada chamada a
next()retorna umResult<DirEntry>— por isso precisamos doentry?. \n entry.path()retorna o caminho completo para o arquivo/pasta. \n - O método
path.is_file()filtra os subdiretórios. \n
Execute o código com o comando cargo run — você verá a lista de arquivos na raiz do projeto (por exemplo, Cargo.toml, src/main.rs).
2.1. Filtragem por extensão
\n\nFrequentemente, precisamos obter apenas arquivos específicos, como apenas .rs ou .txt. Vamos adicionar um filtro:
use std::fs;\nuse std::path::Path;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n let path = Path::new(".");\n\n for entry in fs::read_dir(path)? {\n let entry = entry?;\n let path = entry.path();\n\n // Verifica se é um arquivo e a extensão é .rs\n if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {\n println!("{}", path.display());\n }\n }\n\n Ok(())\n}\n\nO truque com map_or é necessário para tratar com segurança o caso em que o arquivo não tem extensão — path.extension() retorna Option<&OsStr>.
3. Travessia recursiva: explorando todas as subpastas
\n\nO simples read_dir não entra em subdiretórios. Se você precisar coletar todos os arquivos recursivamente (por exemplo, para buscar duplicatas), terá que escrever sua própria função recursiva. Aqui está uma solução elegante:
use std::fs;\nuse std::path::Path;\n\nfn visit_dirs(dir: &Path, depth: usize) -> Result<(), Box&l