---
title: "Módulo std::fs em Rust"
url: "https://rustlang.com.br/stdlib/fs-module/"
markdown_url: "https://rustlang.com.br/stdlib/fs-module.MD"
description: "Referência completa do módulo std::fs em Rust: read_to_string, write, copy, rename, remove, create_dir_all, read_dir, metadata e symlink."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Módulo std::fs em Rust

Referência completa do módulo std::fs em Rust: read_to_string, write, copy, rename, remove, create_dir_all, read_dir, metadata e symlink.


# Módulo std::fs em Rust

O módulo `std::fs` fornece funções de alto nível para operações no sistema de arquivos: ler, escrever, copiar, renomear, remover arquivos e diretórios, consultar metadados e criar links simbólicos. Diferente do tipo `File` (que requer abrir e fechar handles manualmente), as funções de `std::fs` são operações completas em uma única chamada — ideais para tarefas simples e scripts.

## Visão geral e tipos-chave

### Funções de arquivo

- **`fs::read_to_string(path)`** — lê arquivo inteiro como `String` (UTF-8)
- **`fs::read(path)`** — lê arquivo inteiro como `Vec<u8>` (bytes)
- **`fs::write(path, conteudo)`** — escreve conteúdo em arquivo (cria ou sobrescreve)
- **`fs::copy(src, dst)`** — copia arquivo preservando permissões
- **`fs::rename(src, dst)`** — move/renomeia arquivo ou diretório
- **`fs::remove_file(path)`** — remove um arquivo

### Funções de diretório

- **`fs::create_dir(path)`** — cria um diretório (pai deve existir)
- **`fs::create_dir_all(path)`** — cria diretório e todos os pais necessários
- **`fs::remove_dir(path)`** — remove diretório vazio
- **`fs::remove_dir_all(path)`** — remove diretório e todo seu conteúdo
- **`fs::read_dir(path)`** — retorna iterador sobre entradas do diretório

### Funções de metadados

- **`fs::metadata(path)`** — metadados seguindo symlinks
- **`fs::symlink_metadata(path)`** — metadados sem seguir symlinks
- **`fs::set_permissions(path, perm)`** — altera permissões

## Padrões comuns com código

### Leitura e escrita rápida

```rust
use std::fs;
use std::io;

fn main() -> io::Result<()> {
    // Escrever texto em arquivo (cria ou sobrescreve)
    fs::write("notas.txt", "Aprender Rust\nEstudar std::fs\n")?;

    // Ler arquivo inteiro como String
    let conteudo = fs::read_to_string("notas.txt")?;
    println!("Notas ({} bytes):\n{}", conteudo.len(), conteudo);

    // Ler como bytes (para binários)
    let bytes = fs::read("notas.txt")?;
    println!("Primeiros bytes: {:?}", &bytes[..10.min(bytes.len())]);

    Ok(())
}
```

### Gerenciar estrutura de diretórios

```rust
use std::fs;
use std::io;

fn criar_estrutura_projeto(nome: &str) -> io::Result<()> {
    // create_dir_all cria toda a hierarquia de uma vez
    fs::create_dir_all(format!("{}/src/models", nome))?;
    fs::create_dir_all(format!("{}/src/controllers", nome))?;
    fs::create_dir_all(format!("{}/src/views", nome))?;
    fs::create_dir_all(format!("{}/tests", nome))?;
    fs::create_dir_all(format!("{}/docs", nome))?;

    // Criar arquivos iniciais
    fs::write(
        format!("{}/src/main.rs", nome),
        "fn main() {\n    println!(\"Hello, world!\");\n}\n",
    )?;
    fs::write(
        format!("{}/Cargo.toml", nome),
        format!(
            "[package]\nname = \"{}\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
            nome
        ),
    )?;

    println!("Projeto '{}' criado com sucesso", nome);
    Ok(())
}

fn main() -> io::Result<()> {
    criar_estrutura_projeto("meu_app")?;
    Ok(())
}
```

### Listar conteúdo de diretório

```rust
use std::fs;
use std::io;

fn listar_diretorio(caminho: &str) -> io::Result<()> {
    println!("Conteúdo de '{}':", caminho);

    let mut entradas: Vec<_> = fs::read_dir(caminho)?
        .filter_map(|e| e.ok())
        .collect();

    // Ordenar por nome
    entradas.sort_by_key(|e| e.file_name());

    for entrada in &entradas {
        let meta = entrada.metadata()?;
        let tipo = if meta.is_dir() {
            "DIR "
        } else if meta.is_symlink() {
            "LINK"
        } else {
            "FILE"
        };
        let tamanho = if meta.is_file() {
            format!("{:>10} bytes", meta.len())
        } else {
            String::from("           -")
        };

        println!(
            "  [{}] {} {}",
            tipo,
            tamanho,
            entrada.file_name().to_string_lossy()
        );
    }

    println!("Total: {} entradas", entradas.len());
    Ok(())
}

fn main() -> io::Result<()> {
    listar_diretorio(".")?;
    Ok(())
}
```

## Tabela de funções principais

| Função | Retorno | Descrição |
|---|---|---|
| `fs::read_to_string(path)` | `io::Result<String>` | Lê arquivo como texto UTF-8 |
| `fs::read(path)` | `io::Result<Vec<u8>>` | Lê arquivo como bytes |
| `fs::write(path, conteudo)` | `io::Result<()>` | Escreve bytes/string em arquivo |
| `fs::copy(src, dst)` | `io::Result<u64>` | Copia arquivo, retorna bytes copiados |
| `fs::rename(src, dst)` | `io::Result<()>` | Move/renomeia arquivo ou diretório |
| `fs::remove_file(path)` | `io::Result<()>` | Remove arquivo |
| `fs::create_dir(path)` | `io::Result<()>` | Cria diretório (pai deve existir) |
| `fs::create_dir_all(path)` | `io::Result<()>` | Cria diretório com todos os pais |
| `fs::remove_dir(path)` | `io::Result<()>` | Remove diretório vazio |
| `fs::remove_dir_all(path)` | `io::Result<()>` | Remove diretório recursivamente |
| `fs::read_dir(path)` | `io::Result<ReadDir>` | Iterador sobre entradas |
| `fs::metadata(path)` | `io::Result<Metadata>` | Metadados (segue symlinks) |
| `fs::symlink_metadata(path)` | `io::Result<Metadata>` | Metadados (não segue symlinks) |
| `fs::set_permissions(path, perm)` | `io::Result<()>` | Altera permissões |
| `fs::canonicalize(path)` | `io::Result<PathBuf>` | Caminho absoluto canônico |
| `fs::hard_link(src, dst)` | `io::Result<()>` | Cria hard link |
| `fs::soft_link(src, dst)` | `io::Result<()>` | Cria link simbólico (Unix) |

## Exemplos práticos

### Exemplo 1: Copiar e mover arquivos com verificação

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

fn copiar_seguro(origem: &str, destino: &str, sobrescrever: bool) -> io::Result<u64> {
    let path_destino = Path::new(destino);

    if path_destino.exists() && !sobrescrever {
        return Err(io::Error::new(
            io::ErrorKind::AlreadyExists,
            format!("Destino já existe: '{}'", destino),
        ));
    }

    // Garantir que o diretório pai existe
    if let Some(pai) = path_destino.parent() {
        fs::create_dir_all(pai)?;
    }

    let bytes = fs::copy(origem, destino)?;
    println!("Copiado '{}' -> '{}' ({} bytes)", origem, destino, bytes);
    Ok(bytes)
}

fn mover_arquivo(origem: &str, destino: &str) -> io::Result<()> {
    // rename é atômico no mesmo filesystem
    match fs::rename(origem, destino) {
        Ok(()) => {
            println!("Movido '{}' -> '{}'", origem, destino);
            Ok(())
        }
        Err(e) if e.raw_os_error() == Some(18) => {
            // EXDEV: cross-device — copiar e remover
            fs::copy(origem, destino)?;
            fs::remove_file(origem)?;
            println!("Movido (cross-device) '{}' -> '{}'", origem, destino);
            Ok(())
        }
        Err(e) => Err(e),
    }
}

fn main() -> io::Result<()> {
    fs::write("original.txt", "Dados importantes")?;
    copiar_seguro("original.txt", "backup/original.txt", false)?;
    mover_arquivo("original.txt", "arquivo/destino.txt")?;
    Ok(())
}
```

### Exemplo 2: Busca recursiva de arquivos

```rust
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

fn buscar_arquivos(
    diretorio: &Path,
    extensao: &str,
    resultados: &mut Vec<PathBuf>,
) -> io::Result<()> {
    if !diretorio.is_dir() {
        return Ok(());
    }

    for entrada in fs::read_dir(diretorio)? {
        let entrada = entrada?;
        let caminho = entrada.path();

        if caminho.is_dir() {
            buscar_arquivos(&caminho, extensao, resultados)?;
        } else if caminho.extension().and_then(|e| e.to_str()) == Some(extensao) {
            resultados.push(caminho);
        }
    }

    Ok(())
}

fn main() -> io::Result<()> {
    let mut arquivos_rs = Vec::new();
    buscar_arquivos(Path::new("."), "rs", &mut arquivos_rs)?;

    println!("Arquivos .rs encontrados:");
    for arquivo in &arquivos_rs {
        let meta = fs::metadata(arquivo)?;
        println!("  {} ({} bytes)", arquivo.display(), meta.len());
    }
    println!("Total: {} arquivos", arquivos_rs.len());

    Ok(())
}
```

### Exemplo 3: Limpeza de arquivos temporários

```rust
use std::fs;
use std::io;
use std::path::Path;
use std::time::{Duration, SystemTime};

fn limpar_antigos(diretorio: &str, max_idade: Duration) -> io::Result<(usize, u64)> {
    let agora = SystemTime::now();
    let mut removidos = 0usize;
    let mut bytes_liberados = 0u64;

    for entrada in fs::read_dir(diretorio)? {
        let entrada = entrada?;
        let meta = entrada.metadata()?;

        if !meta.is_file() {
            continue;
        }

        let modificado = meta.modified()?;
        let idade = agora
            .duration_since(modificado)
            .unwrap_or(Duration::ZERO);

        if idade > max_idade {
            let tamanho = meta.len();
            let nome = entrada.file_name();
            match fs::remove_file(entrada.path()) {
                Ok(()) => {
                    println!("  Removido: {} ({} bytes)", nome.to_string_lossy(), tamanho);
                    removidos += 1;
                    bytes_liberados += tamanho;
                }
                Err(e) => {
                    eprintln!("  Erro ao remover {}: {}", nome.to_string_lossy(), e);
                }
            }
        }
    }

    Ok((removidos, bytes_liberados))
}

fn main() -> io::Result<()> {
    let max_dias = Duration::from_secs(30 * 24 * 60 * 60); // 30 dias
    let (removidos, bytes) = limpar_antigos("/tmp/meu_app", max_dias)?;
    println!(
        "Limpeza: {} arquivos removidos, {:.2} MB liberados",
        removidos,
        bytes as f64 / 1_048_576.0
    );
    Ok(())
}
```

### Exemplo 4: Backup incremental de diretório

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

fn backup_diretorio(origem: &Path, destino: &Path) -> io::Result<(usize, usize)> {
    fs::create_dir_all(destino)?;

    let mut copiados = 0usize;
    let mut ignorados = 0usize;

    for entrada in fs::read_dir(origem)? {
        let entrada = entrada?;
        let tipo = entrada.file_type()?;
        let nome = entrada.file_name();
        let dest_path = destino.join(&nome);

        if tipo.is_dir() {
            let (c, i) = backup_diretorio(&entrada.path(), &dest_path)?;
            copiados += c;
            ignorados += i;
        } else if tipo.is_file() {
            let precisa_copiar = if dest_path.exists() {
                let meta_orig = entrada.metadata()?;
                let meta_dest = fs::metadata(&dest_path)?;
                // Copiar se o original for mais recente ou tamanho diferente
                meta_orig.len() != meta_dest.len()
                    || meta_orig.modified()? > meta_dest.modified()?
            } else {
                true
            };

            if precisa_copiar {
                fs::copy(entrada.path(), &dest_path)?;
                copiados += 1;
            } else {
                ignorados += 1;
            }
        }
    }

    Ok((copiados, ignorados))
}

fn main() -> io::Result<()> {
    let (copiados, ignorados) = backup_diretorio(
        Path::new("projeto"),
        Path::new("backup/projeto"),
    )?;
    println!("Backup: {} copiados, {} sem alteração", copiados, ignorados);
    Ok(())
}
```

### Exemplo 5: Calcular tamanho total de diretório

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

fn tamanho_diretorio(caminho: &Path) -> io::Result<u64> {
    let mut total = 0u64;

    if caminho.is_file() {
        return Ok(fs::metadata(caminho)?.len());
    }

    for entrada in fs::read_dir(caminho)? {
        let entrada = entrada?;
        let meta = entrada.metadata()?;

        if meta.is_file() {
            total += meta.len();
        } else if meta.is_dir() {
            total += tamanho_diretorio(&entrada.path())?;
        }
    }

    Ok(total)
}

fn formatar_tamanho(bytes: u64) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;

    if bytes >= GB {
        format!("{:.2} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.2} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.2} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} bytes", bytes)
    }
}

fn main() -> io::Result<()> {
    let caminhos = vec!["src", "target", "docs"];
    for caminho in caminhos {
        let path = Path::new(caminho);
        if path.exists() {
            let tamanho = tamanho_diretorio(path)?;
            println!("{}: {}", caminho, formatar_tamanho(tamanho));
        } else {
            println!("{}: (não encontrado)", caminho);
        }
    }
    Ok(())
}
```

## Padrões de tratamento de erro para I/O

### remove_dir_all com tratamento cuidadoso

```rust
use std::fs;
use std::io;

fn remover_seguro(caminho: &str) -> io::Result<()> {
    match fs::remove_dir_all(caminho) {
        Ok(()) => {
            println!("Removido: '{}'", caminho);
            Ok(())
        }
        Err(e) if e.kind() == io::ErrorKind::NotFound => {
            println!("'{}' já não existe — nada a fazer", caminho);
            Ok(()) // não é erro se já não existia
        }
        Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
            eprintln!("Sem permissão para remover: '{}'", caminho);
            Err(e)
        }
        Err(e) => Err(e),
    }
}
```

### Verificação antes de operações destrutivas

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

fn substituir_arquivo(caminho: &str, novo_conteudo: &str) -> io::Result<()> {
    let path = Path::new(caminho);

    // Criar backup se existir
    if path.exists() {
        let backup = format!("{}.bak", caminho);
        fs::copy(caminho, &backup)?;
        println!("Backup criado: {}", backup);
    }

    fs::write(caminho, novo_conteudo)?;
    println!("Arquivo atualizado: {}", caminho);
    Ok(())
}
```

## Dicas de desempenho

- **`fs::read_to_string` e `fs::write` são adequados para arquivos pequenos** (< 10 MB). Para arquivos maiores, use `File` com `BufReader`/`BufWriter`.
- **`read_dir` é lazy** — as entradas são lidas sob demanda. Colete em `Vec` apenas se precisar ordenar ou acessar múltiplas vezes.
- **`fs::copy` é otimizada** pelo sistema operacional em muitas plataformas (usa `sendfile` no Linux, por exemplo).
- **`rename` é atômico** no mesmo filesystem — use para atualizações seguras de arquivos.

## Veja também

- [File e OpenOptions](/stdlib/file/) — controle fino sobre abertura de arquivos
- [Path e PathBuf](/stdlib/path/) — manipulação de caminhos de arquivo
- [BufReader e BufWriter](/stdlib/bufreader-bufwriter/) — I/O bufferizado para performance
- [Módulo std::io](/stdlib/io-module/) — traits e tipos de I/O
- [Escrever em Arquivo](/receitas/escrever-arquivo/) — receita prática de escrita
- Documentação oficial: [`std::fs`](https://doc.rust-lang.org/std/fs/)
