Revisão da Memória de Programa

Os programas alocam memória de duas maneiras:

  • Pilha: Área contínua de memória para variáveis locais.

    • Os valores têm tamanhos fixos conhecidos em tempo de compilação.
    • Extremamente rápida: basta mover um ponteiro de pilha.
    • Fácil de gerenciar: segue chamadas de função.
    • Ótima localidade de memória.
  • Heap: Armazenamento de valores fora das chamadas de função.

    • Valores possuem tamanhos dinâmicos determinados em tempo de execução.
    • Ligeiramente mais devagar que a pilha: é necessário um pouco de gerenciamento.
    • Sem garantias de localidade de memória.

Exemplo

A criação de uma String coloca metadados de tamanho fixo na pilha e dados dinamicamente dimensionados - a string propriamente dita - no heap:

fn main() {
    let s1 = String::from("Olá");
}
StackHeaps1capacity5ptrHellolen5
This slide should take about 5 minutes.
  • Mencione que uma String é suportada por um Vec, portanto ela tem um tamanho e capacidade e pode crescer se for mutável por meio de realocação no heap.

  • Se os alunos perguntarem sobre isso, você pode mencionar que a memória subjacente é alocada no heap usando o System Allocator e os alocadores personalizados podem ser implementados usando a API Allocator.

Mais para Explorar

Podemos inspecionar o layout da memória com Rust inseguro (unsafe). No entanto, você deve apontar que isso é legitimamente inseguro!

fn main() {
    let mut s1 = String::from("Olá");
    s1.push(' ');
    s1.push_str("mundo");
    // NÃO FAÇA ISSO EM CASA! Somente com propósito educacional.
    // String não fornece nenhuma garantia sobre o seu layout, então isso pode causar
    // um comportamento indefinido.
    unsafe {
        let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);
        println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}");
    }
}