Desbravando Rust

Gerenciamento de Memória em Rust vs Python: Alocação e Desempenho

🔍 Imagine que a memória do computador é como uma grande biblioteca. Em Python, temos um bibliotecário (o Garbage Collector) que fica organizando os livros (objetos) para nós. Já em Rust, somos nós mesmos os bibliotecários, mas com regras muito claras sobre quem pode pegar qual livro e quando devolver. Vamos desvendar essas diferenças fundamentais!

Introdução: Duas Filosofias, Dois Mundos

Na programação, gerenciamento de memória é como administrar os recursos de uma cidade:

Vamos explorar como essas abordagens impactam desempenho, segurança e produtividade.

🐍 Seção 1: O Mundo Python - Garbage Collector e Referências

Python usa um sistema automático de gerenciamento de memória baseado em:

# Exemplo de alocação em Python
lista = [1, 2, 3]  # Aloca memória para a lista
outra_ref = lista   # Cria nova referência para o mesmo objeto
del lista           # Remove uma referência, mas o objeto persiste
# O GC eventualmente limpará a memória quando não houver mais referências

Problema clássico em Python:

# Ciclo de referências que o GC precisa resolver
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

a = Node(1)
b = Node(2)
a.next = b
b.next = a  # Ciclo criado!

# Mesmo sem referências externas, o GC precisa intervir
del a
del b

Vantagens do modelo Python:

Desvantagens:

🦀 Seção 2: O Modelo Rust - Ownership e Borrowing

Rust introduz um sistema único de propriedade (ownership) com 3 regras fundamentais:

  1. Cada valor em Rust tem um dono (owner)
  2. Só pode haver um dono por vez
  3. Quando o dono sai de escopo, o valor é liberado
// Exemplo básico de ownership
fn main() {
    let s = String::from("hello");  // s é o dono da String
    faz_algo(s);                    // Transfere a propriedade para a função

    // println!("{}", s);          // ERRO! s não é mais dono do valor
}

fn faz_algo(texto: String) {        // texto agora é o novo dono
    println!("{}", texto);
}                                   // texto sai de escopo e a memória é liberada

Borrowing (empréstimo): Quando não queremos transferir a propriedade:

fn main() {
    let s = String::from("hello");
    pega_emprestado(&s);            // Empresta uma referência imutável
    println!("{}", s);              // OK! s ainda é o dono
}

fn pega_emprestado(texto: &String) { // Recebe uma referência
    println!("{}", texto);
}

🔄 Comparação Direta: Python vs Rust

Vamos implementar um exemplo prático em ambas as linguagens:

Cenário: Processar um grande conjunto de dados sem desperdiçar memória

Versão Python:

def processar_dados():
    dados = [x for x in range(1_000_000)]  # Aloca lista grande

    # Modifica os dados
    resultados = [x * 2 for x in dados]

    # Liberação da memória depende do GC
    return resultados

resultado = processar_dados()
# Quando 'resultado' não for mais usado, o GC liberará a memória

Versão Rust:

fn processar_dados() -> Vec<i32> {
    let dados: Vec<i32> = (1..1_000_000).collect();  // Aloca vetor

    // Consome 'dados' e produz novo vetor
    let resultados: Vec<i32> = dados.into_iter().map(|x| x * 2).collect();

    // 'dados' foi movido e liberado automaticamente
    resultados
}  // 'resultados' será liberado quando sair do escopo do chamador

fn main() {
    let resultado = processar_dados();
    // Memória gerenciada de forma previsível
}

Principais diferenças: | Característica | Python | Rust | |———————-|————————-|————————–| | Modelo | Garbage Collected | Ownership based | | Controle | Automático | Manual (com segurança) | | Previsibilidade | Baixa | Alta | | Overhead | Alto (GC) | Praticamente zero | | Segurança | Possíveis vazamentos | Garantida em tempo de compilação |

🚨 Erros Comuns de Pythonistas em Rust

  1. Tentar modificar dados emprestados:
    let mut s = String::from("hello");
    let ref1 = &s;
    let ref2 = &mut s;  // ERRO! Não pode ter referência mutável enquanto há imutáveis
    
  2. Assumir que cópias são baratas como em Python:
    let s1 = String::from("texto grande");
    let s2 = s1;  // Em Rust isso MOVE, não copia (a menos que implemente Clone)
    
  3. Esquecer que escopos determinam lifetime:
    let r;
    {
     let x = 5;
     r = &x;  // ERRO! x não vive o suficiente
    }
    

🔍 Exemplo Prático Completo: Processador de Dados

Vamos implementar um sistema mais complexo em ambas as linguagens:

Python (com gerenciamento manual):

class DataProcessor:
    def __init__(self):
        self.cache = {}

    def process(self, data):
        # Verifica cache primeiro
        if id(data) in self.cache:
            return self.cache[id(data)]

        # Processamento pesado
        result = [x ** 2 for x in data]

        # Guarda no cache (cuidado com memory leak!)
        self.cache[id(data)] = result
        return result

# Uso
processor = DataProcessor()
dados = list(range(100000))
resultado = processor.process(dados)
del dados  # Importante para liberar memória

Rust (com segurança garantida):

use std::collections::HashMap;

struct DataProcessor {
    cache: HashMap<usize, Vec<i32>>,
}

impl DataProcessor {
    fn new() -> Self {
        DataProcessor {
            cache: HashMap::new(),
        }
    }

    fn process(&mut self, data: &[i32]) -> &Vec<i32> {
        let data_ptr = data.as_ptr() as usize;

        // Verifica cache primeiro (usando entry API para eficiência)
        self.cache.entry(data_ptr)
            .or_insert_with(|| data.iter().map(|&x| x.pow(2)).collect())
    }
}

fn main() {
    let mut processor = DataProcessor::new();
    let dados: Vec<i32> = (0..100000).collect();
    let resultado = processor.process(&dados);

    // Memória gerenciada automaticamente com garantias
}

Análise:

📊 Quando Escolher Cada Abordagem

Prefira Python quando:

Prefira Rust quando:

🎯 O que aprendemos

✔ Python usa Garbage Collector automático enquanto Rust usa ownership ✔ Contagem de referências vs verificação em tempo de compilação ✔ Rust oferece melhor desempenho e previsibilidade na gestão de memória ✔ Python prioriza produtividade em detrimento do controle fino ✔ O modelo de ownership previne bugs comuns como dangling pointers ✔ Borrowing em Rust permite segurança sem sacrificar flexibilidade

📚 Quer Aprofundar em Rust?

Este post é apenas um aperitivo do conteúdo completo que oferecemos no livro “Desbravando Rust”! Se você quer:

Visite nosso site em desbravandorust.com.br e garanta seu exemplar hoje mesmo! 🚀

Próximo post: “Concorrência em Rust vs Python: Threads, async e além”. Não perca!