Ler Arquivo CSV em Rust
A crate csv é a solução padrão para trabalhar com arquivos CSV em Rust. Combinada com serde, ela permite deserializar linhas do CSV diretamente em structs tipadas.
Dependências
Cargo.toml:
[dependencies]
csv = "1"
serde = { version = "1", features = ["derive"] }
Leitura básica de CSV
Leia um arquivo CSV registro por registro:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// Criar arquivo CSV de exemplo
std::fs::write("produtos.csv", "\
nome,preco,estoque
Notebook,4599.90,15
Mouse,89.99,150
Teclado,249.50,80
Monitor,1899.00,25
Headset,399.90,60
")?;
let mut leitor = csv::Reader::from_path("produtos.csv")?;
// Ler cabeçalhos
println!("Colunas: {:?}", leitor.headers()?);
println!();
// Iterar registros como StringRecord
for resultado in leitor.records() {
let registro = resultado?;
println!(
"Produto: {}, Preço: R${}, Estoque: {}",
®istro[0], ®istro[1], ®istro[2]
);
}
Ok(())
}
Saída:
Colunas: StringRecord(["nome", "preco", "estoque"])
Produto: Notebook, Preço: R$4599.90, Estoque: 15
Produto: Mouse, Preço: R$89.99, Estoque: 150
Produto: Teclado, Preço: R$249.50, Estoque: 80
Produto: Monitor, Preço: R$1899.00, Estoque: 25
Produto: Headset, Preço: R$399.90, Estoque: 60
Deserialização tipada com serde
A forma mais poderosa: mapeie cada linha do CSV para uma struct Rust:
use serde::Deserialize;
use std::error::Error;
#[derive(Debug, Deserialize)]
struct Produto {
nome: String,
preco: f64,
estoque: u32,
}
fn main() -> Result<(), Box<dyn Error>> {
std::fs::write("produtos.csv", "\
nome,preco,estoque
Notebook,4599.90,15
Mouse,89.99,150
Teclado,249.50,80
Monitor,1899.00,25
Headset,399.90,60
")?;
let mut leitor = csv::Reader::from_path("produtos.csv")?;
let mut total_valor = 0.0;
let mut total_itens = 0u32;
for resultado in leitor.deserialize() {
let produto: Produto = resultado?;
let valor_total = produto.preco * produto.estoque as f64;
total_valor += valor_total;
total_itens += produto.estoque;
println!(
"{:<12} R${:>8.2} x {:>3} = R${:>10.2}",
produto.nome, produto.preco, produto.estoque, valor_total
);
}
println!("{}", "-".repeat(46));
println!("Total: {} itens, R${:.2}", total_itens, total_valor);
Ok(())
}
Saída:
Notebook R$ 4599.90 x 15 = R$ 68998.50
Mouse R$ 89.99 x 150 = R$ 13498.50
Teclado R$ 249.50 x 80 = R$ 19960.00
Monitor R$ 1899.00 x 25 = R$ 47475.00
Headset R$ 399.90 x 60 = R$ 23994.00
----------------------------------------------
Total: 330 itens, R$173926.00
Delimitadores personalizados
Nem todo arquivo usa vírgula. Configure o delimitador com ReaderBuilder:
use csv::ReaderBuilder;
use serde::Deserialize;
use std::error::Error;
#[derive(Debug, Deserialize)]
struct Registro {
data: String,
descricao: String,
valor: f64,
}
fn main() -> Result<(), Box<dyn Error>> {
// CSV separado por ponto e vírgula (comum no Brasil)
std::fs::write("transacoes.csv", "\
data;descricao;valor
2026-02-01;Salário;5000.00
2026-02-05;Aluguel;-1500.00
2026-02-10;Mercado;-450.30
2026-02-15;Freelance;2000.00
")?;
let mut leitor = ReaderBuilder::new()
.delimiter(b';') // ponto e vírgula
.has_headers(true)
.from_path("transacoes.csv")?;
let mut saldo = 0.0;
for resultado in leitor.deserialize() {
let reg: Registro = resultado?;
saldo += reg.valor;
let sinal = if reg.valor >= 0.0 { "+" } else { "" };
println!(
"{} | {:<20} | {}R${:.2} | Saldo: R${:.2}",
reg.data, reg.descricao, sinal, reg.valor, saldo
);
}
Ok(())
}
Saída:
2026-02-01 | Salário | +R$5000.00 | Saldo: R$5000.00
2026-02-05 | Aluguel | R$-1500.00 | Saldo: R$3500.00
2026-02-10 | Mercado | R$-450.30 | Saldo: R$3049.70
2026-02-15 | Freelance | +R$2000.00 | Saldo: R$5049.70
CSV sem cabeçalho
Quando o arquivo não tem linha de cabeçalho:
use csv::ReaderBuilder;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
std::fs::write("numeros.csv", "\
1,2,3
4,5,6
7,8,9
")?;
let mut leitor = ReaderBuilder::new()
.has_headers(false)
.from_path("numeros.csv")?;
for resultado in leitor.records() {
let registro = resultado?;
let soma: i32 = registro.iter()
.filter_map(|v| v.parse::<i32>().ok())
.sum();
println!("{:?} => soma = {}", registro, soma);
}
Ok(())
}
Saída:
StringRecord(["1", "2", "3"]) => soma = 6
StringRecord(["4", "5", "6"]) => soma = 15
StringRecord(["7", "8", "9"]) => soma = 24
Ler CSV de uma String (sem arquivo)
Útil para testes ou dados embutidos no código:
use csv::ReaderBuilder;
use serde::Deserialize;
use std::error::Error;
#[derive(Debug, Deserialize)]
struct Cidade {
nome: String,
estado: String,
populacao: u64,
}
fn main() -> Result<(), Box<dyn Error>> {
let dados = "\
nome,estado,populacao
São Paulo,SP,12400000
Rio de Janeiro,RJ,6750000
Brasília,DF,3100000
Salvador,BA,2900000
Fortaleza,CE,2700000";
let mut leitor = ReaderBuilder::new()
.from_reader(dados.as_bytes());
let mut cidades: Vec<Cidade> = Vec::new();
for resultado in leitor.deserialize() {
cidades.push(resultado?);
}
// Ordenar por população (maior para menor)
cidades.sort_by(|a, b| b.populacao.cmp(&a.populacao));
for (i, cidade) in cidades.iter().enumerate() {
println!(
"{}. {} ({}) - {:.1}M habitantes",
i + 1,
cidade.nome,
cidade.estado,
cidade.populacao as f64 / 1_000_000.0
);
}
Ok(())
}
Saída:
1. São Paulo (SP) - 12.4M habitantes
2. Rio de Janeiro (RJ) - 6.8M habitantes
3. Brasília (DF) - 3.1M habitantes
4. Salvador (BA) - 2.9M habitantes
5. Fortaleza (CE) - 2.7M habitantes
Veja também
- Ler Arquivo em Rust — leitura genérica de arquivos
- Escrever em Arquivo — salve resultados processados
- Converter String para Número — converta campos do CSV
- Dividir String — alternativa manual ao parse de CSV
- Serializar Struct para JSON — converta dados CSV para JSON
- Documentação da crate: csv