Alocação de Memória em Rust: Static, Stack e Heap
Esta é a continuação daquele artigo sobre os primeiros passos com o Cargo e minha ideia de manter a memória dos primeiros passos. Agora vamos falar de um assunto que costuma assustar bastante gente no começo: onde diabos os dados ficam guardados na memória? Calma, não é tão complicado quanto parece. Vem comigo que eu te explico o básico de Static, Stack e Heap sem enrolação.
Visão geral
Em Rust, todo valor mora em algum lugar da memória, e saber onde faz toda a diferença pra entender performance, tamanhos conhecidos em tempo de compilação e como o ownership funciona na prática. Resumindo bem rapidinho, temos três “bairros” onde os dados podem morar:
- Static — o dado nasce junto com o programa e vive até o fim, gravado direto no binário.
- Stack — rapidinha e organizada, tipo uma pilha de pratos: o último que entra é o primeiro que sai.
- Heap — mais flexível, pra dados que crescem, encolhem ou precisam durar mais que o escopo onde nasceram.
Static
Dados marcados como static ficam embutidos direto no binário do programa e vivem do início ao fim da execução, sempre no mesmo endereço de memória. É tipo aquele item fixo que nunca sai do lugar.
static SAUDACAO: &str = "Olá, mundo!";
const LIMITE: u32 = 100;
fn main() {
println!("{}", SAUDACAO);
println!("{}", LIMITE);
}
Dica: na maioria das vezes const já resolve, porque o compilador pode otimizar e colar o valor direto onde ele é usado, sem precisar de um endereço fixo. Use static só quando você realmente precisa de um endereço único, compartilhado pelo programa todo.
Stack
A Stack (pilha) é onde moram as variáveis locais com tamanho fixo e conhecido em tempo de compilação — inteiros, floats, booleanos, chars, arrays de tamanho fixo, esse pessoal. Ela é super rápida porque alocar e liberar memória ali é praticamente só empilhar e desempilhar (push/pop), sem mistério.
fn main() {
let x: i32 = 42;
let y: f64 = 3.14;
let array: [i32; 3] = [1, 2, 3];
println!("{} {} {:?}", x, y, array);
} // x, y e array somem da stack aqui, sem drama
Dica: se o tipo implementa Copy e tem tamanho fixo, ele tende a viver na stack. Quando a função acaba, esses valores são liberados automaticamente — nada de alocação dinâmica envolvida, é tudo bem direto.
Heap
Já a Heap é o lugar pra dados que podem crescer, mudar de tamanho em tempo de execução ou que precisam sobreviver além do escopo onde foram criados. Tipos como String, Vec<T> e Box<T> guardam seu conteúdo aqui.
fn main() {
let s = String::from("Olá, mundo!");
let v: Vec<i32> = vec![1, 2, 3];
let b = Box::new(10);
println!("{} {:?} {}", s, v, b);
} // a heap é liberada quando os valores saem de escopo (Drop)
Dica: na heap, o ponteiro pra esses dados fica guardado na stack, mas o conteúdo de fato (e seu tamanho) mora na heap. É por isso que String e Vec<T> conseguem crescer à vontade, enquanto um &str ou um array [T; N] não têm essa liberdade.
Resumo rápido
- Static: dado fixo, gravado no binário, vive o programa todo.
- Stack: rápida, tamanho fixo conhecido em compilação, some sozinha quando o escopo termina.
- Heap: tamanho dinâmico, controlada pelo ownership e pelo trait Drop quando a hora chega.
Conclusão
No fim das contas, entender a diferença entre Static, Stack e Heap ajuda bastante a entender por que o Rust é tão rápido e também por que o compilador é “chato” com tamanhos e tempos de vida — spoiler: é tudo pra te ajudar. Com a prática isso vai ficando natural, e ter esse resumo por perto facilita muito a vida nos primeiros projetos.



