File e OpenOptions em Rust
O tipo std::fs::File representa um arquivo aberto no sistema operacional. Ele implementa as traits Read, Write e Seek, permitindo leitura, escrita e navegação posicional. Para controle fino sobre como um arquivo é aberto (leitura, escrita, append, truncamento, criação), use std::fs::OpenOptions, que fornece um builder configurável.
Visão geral e tipos-chave
File
File é um handle para um arquivo aberto. Quando o File sai de escopo, o arquivo é fechado automaticamente (via Drop). Os dois métodos estáticos mais usados são:
File::open(path)— abre para leitura (equivale aOpenOptions::new().read(true).open(path))File::create(path)— cria ou trunca para escrita (equivale aOpenOptions::new().write(true).create(true).truncate(true).open(path))
OpenOptions
OpenOptions é um builder que configura como o arquivo será aberto:
read(true)— permite leiturawrite(true)— permite escritaappend(true)— posiciona no final para escrita (implicawrite)truncate(true)— limpa o conteúdo ao abrir (requerwrite)create(true)— cria se não existir (requerwriteouappend)create_new(true)— falha se o arquivo já existir (garante criação exclusiva)
Metadata e Permissions
Metadata— informações sobre o arquivo: tamanho, tipo, timestamps, permissões.Permissions— permite verificar e alterar permissões de leitura/escrita.
Padrões comuns com código
Abrir e ler arquivo
use std::fs::File;
use std::io::{self, Read};
fn main() -> io::Result<()> {
// Abrir para leitura
let mut arquivo = File::open("config.toml")?;
let mut conteudo = String::new();
arquivo.read_to_string(&mut conteudo)?;
println!("Configuração ({} bytes):\n{}", conteudo.len(), conteudo);
Ok(())
}
Criar e escrever arquivo
use std::fs::File;
use std::io::{self, Write};
fn main() -> io::Result<()> {
// File::create trunca se existir, cria se não existir
let mut arquivo = File::create("resultado.txt")?;
writeln!(arquivo, "Relatório gerado em 2026-02-23")?;
writeln!(arquivo, "Total de registros: {}", 42)?;
arquivo.flush()?;
Ok(())
}
Adicionar conteúdo com append
use std::fs::OpenOptions;
use std::io::{self, Write};
fn adicionar_log(caminho: &str, mensagem: &str) -> io::Result<()> {
let mut arquivo = OpenOptions::new()
.append(true)
.create(true)
.open(caminho)?;
writeln!(arquivo, "[{}] {}", chrono_simples(), mensagem)?;
Ok(())
}
fn chrono_simples() -> String {
// Simulação simples de timestamp
"2026-02-23 14:30:00".to_string()
}
fn main() -> io::Result<()> {
adicionar_log("app.log", "Servidor iniciado")?;
adicionar_log("app.log", "Conexão recebida de 192.168.1.10")?;
adicionar_log("app.log", "Processamento concluído")?;
println!("Logs adicionados com sucesso");
Ok(())
}
Uso avançado de OpenOptions
use std::fs::OpenOptions;
use std::io::{self, Read, Write, Seek, SeekFrom};
fn main() -> io::Result<()> {
// Abrir para leitura E escrita
let mut arquivo = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("dados.bin")?;
// Escrever dados iniciais
arquivo.write_all(b"AAAAAAAAAA")?;
// Voltar ao início
arquivo.seek(SeekFrom::Start(0))?;
// Ler o que foi escrito
let mut conteudo = String::new();
arquivo.read_to_string(&mut conteudo)?;
println!("Conteúdo: {}", conteudo);
// Sobrescrever parcialmente (posição 3)
arquivo.seek(SeekFrom::Start(3))?;
arquivo.write_all(b"BBB")?;
// Ler tudo novamente
arquivo.seek(SeekFrom::Start(0))?;
conteudo.clear();
arquivo.read_to_string(&mut conteudo)?;
println!("Após edição: {}", conteudo); // AAABBBAAAA
Ok(())
}
Tabela de métodos e funções
Métodos de File
| Método | Descrição |
|---|---|
File::open(path) | Abre para leitura |
File::create(path) | Cria/trunca para escrita |
File::create_new(path) | Cria novo arquivo, falha se existir |
metadata() | Retorna metadados (tamanho, permissões, etc.) |
set_permissions(perm) | Altera permissões do arquivo |
set_len(size) | Define o tamanho do arquivo (trunca ou estende) |
sync_all() | Sincroniza dados e metadados com o disco |
sync_data() | Sincroniza apenas dados com o disco |
try_clone() | Cria handle duplicado para o mesmo arquivo |
set_modified(time) | Define timestamp de modificação |
Métodos de OpenOptions
| Método | Descrição |
|---|---|
OpenOptions::new() | Cria builder com todas opções desativadas |
read(bool) | Habilita leitura |
write(bool) | Habilita escrita |
append(bool) | Escrita no final (implica write) |
truncate(bool) | Limpa conteúdo ao abrir |
create(bool) | Cria se não existir |
create_new(bool) | Falha se já existir |
open(path) | Abre o arquivo com as opções configuradas |
Métodos de Seek
| Método | Descrição |
|---|---|
seek(SeekFrom::Start(n)) | Posiciona em N bytes do início |
seek(SeekFrom::End(n)) | Posiciona em N bytes do final |
seek(SeekFrom::Current(n)) | Move N bytes da posição atual |
rewind() | Volta ao início (equivale a seek(Start(0))) |
stream_position() | Retorna a posição atual |
Exemplos práticos
Exemplo 1: Criação exclusiva de arquivo (evitar sobrescrita)
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
fn criar_arquivo_seguro(caminho: &str, conteudo: &str) -> io::Result<()> {
// create_new() falha se o arquivo já existir
match File::create_new(caminho) {
Ok(mut arquivo) => {
arquivo.write_all(conteudo.as_bytes())?;
println!("Arquivo '{}' criado com sucesso", caminho);
Ok(())
}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
eprintln!("Arquivo '{}' já existe — não sobrescrevendo", caminho);
Err(e)
}
Err(e) => Err(e),
}
}
fn main() -> io::Result<()> {
criar_arquivo_seguro("relatorio_2026.txt", "Dados do relatório...")?;
Ok(())
}
Exemplo 2: Arquivo de configuração com leitura/escrita
use std::fs::OpenOptions;
use std::io::{self, BufRead, BufReader, Seek, SeekFrom, Write};
fn atualizar_config(caminho: &str, chave: &str, novo_valor: &str) -> io::Result<bool> {
let mut arquivo = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(caminho)?;
let leitor = BufReader::new(&arquivo);
let mut linhas: Vec<String> = Vec::new();
let mut encontrou = false;
for resultado in leitor.lines() {
let linha = resultado?;
if linha.starts_with(&format!("{}=", chave)) || linha.starts_with(&format!("{} =", chave)) {
linhas.push(format!("{} = {}", chave, novo_valor));
encontrou = true;
} else {
linhas.push(linha);
}
}
if !encontrou {
linhas.push(format!("{} = {}", chave, novo_valor));
}
// Reescrever o arquivo
arquivo.seek(SeekFrom::Start(0))?;
arquivo.set_len(0)?; // truncar
for linha in &linhas {
writeln!(arquivo, "{}", linha)?;
}
arquivo.sync_all()?;
Ok(encontrou)
}
fn main() -> io::Result<()> {
let atualizado = atualizar_config("app.conf", "porta", "8080")?;
if atualizado {
println!("Configuração atualizada");
} else {
println!("Nova configuração adicionada");
}
Ok(())
}
Exemplo 3: Consultar metadados e permissões
use std::fs;
use std::io;
fn exibir_info_arquivo(caminho: &str) -> io::Result<()> {
let metadata = fs::metadata(caminho)?;
println!("Arquivo: {}", caminho);
println!(" Tamanho: {} bytes", metadata.len());
println!(" É arquivo: {}", metadata.is_file());
println!(" É diretório: {}", metadata.is_dir());
println!(" É symlink: {}", metadata.is_symlink());
println!(" Somente leitura: {}", metadata.permissions().readonly());
if let Ok(modificado) = metadata.modified() {
println!(" Modificado: {:?}", modificado);
}
if let Ok(criado) = metadata.created() {
println!(" Criado: {:?}", criado);
}
Ok(())
}
fn tornar_somente_leitura(caminho: &str) -> io::Result<()> {
let metadata = fs::metadata(caminho)?;
let mut permissoes = metadata.permissions();
permissoes.set_readonly(true);
fs::set_permissions(caminho, permissoes)?;
println!("'{}' agora é somente leitura", caminho);
Ok(())
}
fn main() -> io::Result<()> {
exibir_info_arquivo("Cargo.toml")?;
Ok(())
}
Exemplo 4: Seek para edição em arquivos binários
use std::fs::OpenOptions;
use std::io::{self, Read, Seek, SeekFrom, Write};
/// Lê e modifica um campo em posição fixa de um arquivo binário
fn editar_campo_binario(
caminho: &str,
offset: u64,
novo_valor: &[u8],
) -> io::Result<Vec<u8>> {
let mut arquivo = OpenOptions::new()
.read(true)
.write(true)
.open(caminho)?;
// Ler valor antigo
arquivo.seek(SeekFrom::Start(offset))?;
let mut valor_antigo = vec![0u8; novo_valor.len()];
arquivo.read_exact(&mut valor_antigo)?;
// Escrever novo valor
arquivo.seek(SeekFrom::Start(offset))?;
arquivo.write_all(novo_valor)?;
arquivo.sync_data()?;
println!(
"Offset {}: {:02X?} -> {:02X?}",
offset, valor_antigo, novo_valor
);
Ok(valor_antigo)
}
fn main() -> io::Result<()> {
// Criar arquivo de exemplo
let mut f = std::fs::File::create("teste.bin")?;
f.write_all(&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07])?;
drop(f);
// Editar bytes na posição 2
let antigo = editar_campo_binario("teste.bin", 2, &[0xFF, 0xFE])?;
println!("Valor anterior: {:02X?}", antigo);
Ok(())
}
Exemplo 5: sync_all para garantia de persistência
use std::fs::File;
use std::io::{self, Write};
/// Escrita durável — garante que dados estejam no disco físico
fn escrever_duravel(caminho: &str, dados: &[u8]) -> io::Result<()> {
let mut arquivo = File::create(caminho)?;
arquivo.write_all(dados)?;
// sync_all() garante que dados E metadados estejam no disco
// Importante para bancos de dados, logs de transação, etc.
arquivo.sync_all()?;
Ok(())
}
/// Escrita atômica via arquivo temporário + rename
fn escrever_atomico(caminho: &str, dados: &[u8]) -> io::Result<()> {
let caminho_temp = format!("{}.tmp", caminho);
// 1. Escrever no arquivo temporário
let mut temp = File::create(&caminho_temp)?;
temp.write_all(dados)?;
temp.sync_all()?;
drop(temp);
// 2. Renomear atomicamente (no mesmo filesystem)
std::fs::rename(&caminho_temp, caminho)?;
Ok(())
}
fn main() -> io::Result<()> {
escrever_duravel("dados_importantes.dat", b"dados criticos")?;
escrever_atomico("config.json", b"{\"chave\": \"valor\"}")?;
println!("Escritas duráveis concluídas");
Ok(())
}
Padrões de tratamento de erro para I/O
Erros comuns ao trabalhar com arquivos
use std::fs::File;
use std::io;
fn abrir_com_diagnostico(caminho: &str) -> io::Result<File> {
File::open(caminho).map_err(|e| {
match e.kind() {
io::ErrorKind::NotFound => {
eprintln!("Arquivo não encontrado: '{}'", caminho);
eprintln!("Verifique o caminho e tente novamente.");
}
io::ErrorKind::PermissionDenied => {
eprintln!("Sem permissão para abrir: '{}'", caminho);
eprintln!("Verifique as permissões do arquivo.");
}
_ => {
eprintln!("Erro inesperado ao abrir '{}': {}", caminho, e);
}
}
e
})
}
Padrão try_clone para leitura e escrita simultâneas
use std::fs::OpenOptions;
use std::io::{self, BufRead, BufReader, Write, Seek, SeekFrom};
fn ler_e_registrar(caminho_dados: &str, caminho_log: &str) -> io::Result<()> {
let arquivo = OpenOptions::new().read(true).open(caminho_dados)?;
// Clonar handle para usar em dois contextos
let clone = arquivo.try_clone()?;
let leitor = BufReader::new(arquivo);
let mut log = std::fs::File::create(caminho_log)?;
for (i, linha) in leitor.lines().enumerate() {
let linha = linha?;
writeln!(log, "Linha {}: {} chars", i + 1, linha.len())?;
}
Ok(())
}
Dicas de desempenho
- Use
BufReadereBufWriterao fazer múltiplas operações pequenas sobre umFile. A diferença pode ser de 10x a 100x em performance. - Use
sync_data()em vez desync_all()quando não precisar sincronizar metadados — é mais rápido em alguns sistemas de arquivos. - Pré-aloque com
set_len()quando souber o tamanho final do arquivo para evitar fragmentação. - Escrita atômica via rename é mais segura que sobrescrever diretamente — evita corrupção em caso de falha de energia.
Veja também
- Módulo std::io — visão geral do sistema de I/O
- Read e Write Traits — traits implementadas por File
- BufReader e BufWriter — buffering para performance
- Módulo std::fs — operações de filesystem de alto nível
- Path e PathBuf — manipulação de caminhos
- Ler Arquivo em Rust — receita prática de leitura
- Escrever em Arquivo — receita prática de escrita
- Documentação oficial:
std::fs::File,std::fs::OpenOptions