Ler Arquivo CSV em Rust

Aprenda como ler arquivo CSV em Rust usando a crate csv com serde, cabeçalhos, delimitadores personalizados e tratamento de erros completo.

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: {}",
            &registro[0], &registro[1], &registro[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