Rust para Data Science: Guia Completo 2026 | Rust Brasil

Rust para data science: Polars, ndarray, linfa e processamento de dados. Performance de C com segurança de memória.

Introdução

Python domina o ecossistema de data science e machine learning, e isso não vai mudar tão cedo. Porém, um segredo que poucos conhecem é que muitas das bibliotecas Python mais rápidas já são escritas em Rust por baixo. O Polars (DataFrames ultrarrápidos), o tokenizers da Hugging Face, o Ruff (linter Python mais rápido do mundo) e o uv (gerenciador de pacotes Python) são todos projetos Rust que expõem interfaces Python via PyO3.

Rust está ocupando um nicho estratégico em data science: o backend de alta performance. Quando seus pipelines de dados precisam processar bilhões de registros, quando seu modelo de ML precisa de inferência em tempo real com latência de milissegundos, ou quando você precisa deployar em ambientes com memória limitada (edge/IoT), Rust oferece uma alternativa que pode ser 10-100x mais rápida que Python puro.

O Ecossistema Rust para Data Science

Manipulação de Dados

BibliotecaEquivalente PythonDescrição
PolarspandasDataFrames com execução lazy, multithreaded e sem GIL
ndarrayNumPyArrays N-dimensionais com operações vetorizadas
csvcsv moduleLeitura/escrita de CSV extremamente rápida
arrow-rsPyArrowImplementação do Apache Arrow em Rust

Machine Learning

BibliotecaEquivalente PythonDescrição
BurnPyTorchFramework de deep learning nativo Rust
CandlePyTorch (inferência)Framework ML minimalista da Hugging Face
Linfascikit-learnML clássico (regressão, clustering, classificação)
smartcorescikit-learnAlternativa com foco em simplicidade
tch-rsPyTorchBindings para libtorch (PyTorch C++)

Interoperabilidade com Python

FerramentaFunção
PyO3Escrever extensões Python em Rust
maturinBuild tool para publicar crates Rust como pacotes pip

Cargo.toml Base para Data Science

[package]
name = "analise-dados"
version = "0.1.0"
edition = "2021"

[dependencies]
polars = { version = "0.46", features = ["lazy", "csv", "json", "dtype-date", "dtype-datetime"] }
ndarray = "0.16"
linfa = "0.7"
linfa-linear = "0.7"
csv = "1"
serde = { version = "1", features = ["derive"] }
anyhow = "1"

Exemplo Prático: Análise de Dados com Polars

Polars é a estrela do ecossistema Rust para data science. Oferece uma API expressiva para manipulação de DataFrames com performance que supera pandas em praticamente todos os benchmarks.

use polars::prelude::*;
use anyhow::Result;

fn main() -> Result<()> {
    // === Carregar dados de CSV ===
    let df = CsvReadOptions::default()
        .with_has_header(true)
        .try_into_reader_with_file_path(Some("vendas.csv".into()))?
        .finish()?;

    println!("Shape: {:?}", df.shape());
    println!("Primeiras linhas:\n{}", df.head(Some(5)));

    // === Análise com API Lazy (otimização automática de queries) ===
    let resultado = df.clone().lazy()
        // Filtrar vendas acima de R$ 100
        .filter(col("valor").gt(lit(100.0)))
        // Agrupar por categoria
        .group_by([col("categoria")])
        .agg([
            // Total de vendas por categoria
            col("valor").sum().alias("total_vendas"),
            // Média de valor
            col("valor").mean().alias("media_valor"),
            // Quantidade de transações
            col("valor").count().alias("quantidade"),
            // Maior venda
            col("valor").max().alias("maior_venda"),
        ])
        // Ordenar por total de vendas (decrescente)
        .sort(["total_vendas"], SortMultipleOptions::default().with_order_descending(true))
        .collect()?;

    println!("\nAnálise por categoria:\n{}", resultado);

    // === Criar novas colunas (feature engineering) ===
    let df_features = df.lazy()
        .with_columns([
            // Desconto estimado
            (col("valor") * lit(0.1)).alias("desconto"),
            // Categorização de valor
            when(col("valor").gt(lit(500.0)))
                .then(lit("premium"))
                .when(col("valor").gt(lit(100.0)))
                .then(lit("medio"))
                .otherwise(lit("basico"))
                .alias("segmento"),
        ])
        .collect()?;

    println!("\nCom features:\n{}", df_features.head(Some(5)));

    // === Estatísticas descritivas ===
    let stats = df.describe(None)?;
    println!("\nEstatísticas descritivas:\n{}", stats);

    // === Exportar resultado ===
    let mut arquivo = std::fs::File::create("resultado_analise.csv")?;
    CsvWriter::new(&mut arquivo)
        .finish(&mut resultado.clone())?;

    println!("\nResultado exportado para resultado_analise.csv");
    Ok(())
}

Polars: Performance Comparada com pandas

Operação (10M linhas)pandasPolars (Rust)Speedup
Leitura CSV8.2s0.9s9x
Group by + agregação3.1s0.2s15x
Join de dois DataFrames5.5s0.4s14x
Filter + sort2.8s0.15s19x
Uso de memória4.2 GB1.1 GB4x menos

Machine Learning com Linfa

Linfa é o equivalente Rust do scikit-learn, oferecendo algoritmos clássicos de ML:

use linfa::prelude::*;
use linfa_linear::LinearRegression;
use ndarray::{array, Array1, Array2};

fn main() -> anyhow::Result<()> {
    // Dados de treino: área (m²) -> preço (R$ mil)
    let features: Array2<f64> = array![
        [45.0],
        [60.0],
        [75.0],
        [90.0],
        [110.0],
        [130.0],
        [150.0],
        [180.0],
    ];

    let targets: Array1<f64> = array![
        180.0, 240.0, 310.0, 380.0, 460.0, 540.0, 620.0, 750.0
    ];

    // Criar dataset
    let dataset = Dataset::new(features.clone(), targets.clone());

    // Treinar modelo de regressão linear
    let modelo = LinearRegression::default()
        .fit(&dataset)?;

    // Fazer previsões
    let novos_imoveis: Array2<f64> = array![
        [50.0],
        [100.0],
        [200.0],
    ];

    let previsoes = modelo.predict(&novos_imoveis);

    println!("=== Previsão de Preços de Imóveis ===\n");
    let areas = [50.0, 100.0, 200.0];
    for (area, preco) in areas.iter().zip(previsoes.iter()) {
        println!("  Área: {}m² → Preço estimado: R$ {:.0} mil", area, preco);
    }

    // Avaliar modelo nos dados de treino
    let previsoes_treino = modelo.predict(&features);
    let residuos: Array1<f64> = &targets - &previsoes_treino;
    let mse = residuos.mapv(|r| r * r).mean().unwrap();
    let rmse = mse.sqrt();

    println!("\n=== Métricas do Modelo ===");
    println!("  RMSE: R$ {:.2} mil", rmse);
    println!("  Erro médio: R$ {:.2} mil", residuos.mapv(f64::abs).mean().unwrap());

    Ok(())
}

Deep Learning com Burn

Burn é um framework de deep learning nativo em Rust, com suporte a múltiplos backends (CPU, CUDA, Wasm):

[dependencies]
burn = { version = "0.16", features = ["train", "ndarray"] }
use burn::prelude::*;
use burn::module::Module;
use burn::nn;
use burn::tensor::backend::Backend;

/// Rede neural para classificação de dígitos (MNIST-like).
#[derive(Module, Debug)]
pub struct ClassificadorDigitos<B: Backend> {
    conv1: nn::conv::Conv2d<B>,
    conv2: nn::conv::Conv2d<B>,
    pool: nn::pool::AdaptiveAvgPool2d,
    dropout: nn::Dropout,
    linear1: nn::Linear<B>,
    linear2: nn::Linear<B>,
    activation: nn::Relu,
}

impl<B: Backend> ClassificadorDigitos<B> {
    pub fn new(device: &B::Device) -> Self {
        let conv1 = nn::conv::Conv2dConfig::new([1, 8], [3, 3])
            .init(device);
        let conv2 = nn::conv::Conv2dConfig::new([8, 16], [3, 3])
            .init(device);
        let pool = nn::pool::AdaptiveAvgPool2dConfig::new([8, 8])
            .init();
        let dropout = nn::DropoutConfig::new(0.5).init();
        let linear1 = nn::LinearConfig::new(16 * 8 * 8, 128)
            .init(device);
        let linear2 = nn::LinearConfig::new(128, 10)
            .init(device);
        let activation = nn::Relu::new();

        Self {
            conv1,
            conv2,
            pool,
            dropout,
            linear1,
            linear2,
            activation,
        }
    }

    pub fn forward(&self, images: Tensor<B, 4>) -> Tensor<B, 2> {
        let x = self.conv1.forward(images);
        let x = self.activation.forward(x);
        let x = self.conv2.forward(x);
        let x = self.activation.forward(x);
        let x = self.pool.forward(x);
        let x = self.dropout.forward(x);

        // Flatten
        let batch_size = x.dims()[0];
        let x = x.reshape([batch_size, 16 * 8 * 8]);

        let x = self.linear1.forward(x);
        let x = self.activation.forward(x);
        let x = self.dropout.forward(x);
        self.linear2.forward(x)
    }
}

Interoperabilidade Python com PyO3

A forma mais pragmática de usar Rust em data science é escrevendo extensões Python. PyO3 permite expor funções Rust como módulos Python nativos:

[package]
name = "meu-modulo-rapido"
version = "0.1.0"
edition = "2021"

[lib]
name = "meu_modulo_rapido"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.23", features = ["extension-module"] }
rayon = "1"
use pyo3::prelude::*;
use rayon::prelude::*;

/// Calcula estatísticas descritivas de forma paralela.
/// Muito mais rápido que Python puro para arrays grandes.
#[pyfunction]
fn estatisticas_rapidas(dados: Vec<f64>) -> PyResult<(f64, f64, f64, f64, f64)> {
    if dados.is_empty() {
        return Err(pyo3::exceptions::PyValueError::new_err(
            "Array vazio"
        ));
    }

    let n = dados.len() as f64;

    // Soma e média (paralelo)
    let soma: f64 = dados.par_iter().sum();
    let media = soma / n;

    // Variância (paralelo)
    let variancia: f64 = dados
        .par_iter()
        .map(|x| (x - media).powi(2))
        .sum::<f64>()
        / n;
    let desvio_padrao = variancia.sqrt();

    // Min e max (paralelo)
    let min = dados.par_iter().cloned().reduce(|| f64::INFINITY, f64::min);
    let max = dados.par_iter().cloned().reduce(|| f64::NEG_INFINITY, f64::max);

    Ok((media, desvio_padrao, min, max, soma))
}

/// Normaliza um array usando z-score (paralelo).
#[pyfunction]
fn normalizar_zscore(dados: Vec<f64>) -> PyResult<Vec<f64>> {
    let n = dados.len() as f64;
    let media: f64 = dados.par_iter().sum::<f64>() / n;
    let desvio: f64 = (dados
        .par_iter()
        .map(|x| (x - media).powi(2))
        .sum::<f64>()
        / n)
        .sqrt();

    if desvio == 0.0 {
        return Ok(vec![0.0; dados.len()]);
    }

    Ok(dados
        .par_iter()
        .map(|x| (x - media) / desvio)
        .collect())
}

/// Módulo Python exposto via PyO3.
#[pymodule]
fn meu_modulo_rapido(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(estatisticas_rapidas, m)?)?;
    m.add_function(wrap_pyfunction!(normalizar_zscore, m)?)?;
    Ok(())
}

Usando em Python

# Instalar: pip install maturin && maturin develop
import meu_modulo_rapido
import numpy as np
import time

# Gerar dados
dados = np.random.randn(10_000_000).tolist()

# Benchmark: Rust vs NumPy
inicio = time.time()
media_rust, std_rust, min_rust, max_rust, soma_rust = meu_modulo_rapido.estatisticas_rapidas(dados)
tempo_rust = time.time() - inicio

inicio = time.time()
arr = np.array(dados)
media_np, std_np, min_np, max_np, soma_np = arr.mean(), arr.std(), arr.min(), arr.max(), arr.sum()
tempo_numpy = time.time() - inicio

print(f"Rust:  {tempo_rust:.3f}s")
print(f"NumPy: {tempo_numpy:.3f}s")
print(f"Speedup: {tempo_numpy/tempo_rust:.1f}x")

Empresas Usando Rust em Data Science

  • Hugging Face: Tokenizers library (10x mais rápido que Python), Candle (inferência ML)
  • Pola.rs: Empresa por trás do Polars, financiada para competir com pandas
  • Astral: uv (gerenciador de pacotes Python) e ruff (linter) — ferramentas para o ecossistema Python, escritas em Rust
  • Databricks: Componentes de processamento de dados de alta performance
  • Cloudflare: Modelos de ML para detecção de ameaças no edge
  • Discord: Pipeline de processamento de dados em tempo real

Como Começar

  1. Aprenda Rust: Tutorial de primeiros passos — domine a linguagem antes de mergulhar em data science
  2. Comece com CSV: Use nossa receita de leitura de CSV para entender I/O de dados
  3. Explore Polars: Comece substituindo scripts pandas por Polars em Rust
  4. PyO3 para pragmatismo: Se seu time usa Python, escreva os gargalos em Rust com PyO3
  5. Linfa para ML clássico: Experimente regressão e classificação antes de partir para deep learning
  6. Burn para deep learning: Quando precisar de redes neurais nativas em Rust

Conclusão

Rust não vai substituir Python como a interface de data science — e não precisa. O papel de Rust é ser o motor de alta performance por trás das ferramentas que cientistas de dados usam todos os dias. Se você é um data scientist, aprender Rust com PyO3 permite acelerar seus pipelines em ordens de magnitude. Se você é um desenvolvedor Rust, o ecossistema de data science oferece oportunidades crescentes para construir as ferramentas da próxima geração.


Veja Também