---
title: "Pilha (Stack): Estrutura LIFO em Rust"
url: "https://rustlang.com.br/estruturas-dados/pilha-stack/"
markdown_url: "https://rustlang.com.br/estruturas-dados/pilha-stack.MD"
description: "Guia completo sobre a estrutura de dados Pilha (Stack) em Rust — conceito LIFO, implementação com Vec, algoritmos clássicos como balanceamento de parênteses e avaliação de expressões pós-fixas."
date: ""
author: "Equipe Rust Brasil"
---

# Pilha (Stack): Estrutura LIFO em Rust

Guia completo sobre a estrutura de dados Pilha (Stack) em Rust — conceito LIFO, implementação com Vec, algoritmos clássicos como balanceamento de parênteses e avaliação de expressões pós-fixas.


## Introdução

A **pilha** (stack) é uma das estruturas de dados mais fundamentais e intuitivas da ciência da
computação. Ela segue o princípio **LIFO** — *Last In, First Out* (último a entrar, primeiro a
sair) — da mesma forma que uma pilha de pratos: você só pode colocar ou retirar pratos do topo.

Apesar de sua simplicidade conceitual, a pilha é extraordinariamente versátil. Ela é utilizada
internamente pelo próprio computador (a call stack gerencia chamadas de funções), por compiladores
(análise sintática e avaliação de expressões), navegadores (botão voltar), editores de texto
(desfazer) e em inúmeros algoritmos como DFS (busca em profundidade).

Em Rust, a implementação mais natural e eficiente de uma pilha é usar `Vec<T>` como armazenamento
interno, aproveitando suas operações `push()` e `pop()` que já operam no final do vetor em O(1).
Neste artigo, construiremos uma pilha genérica, exploraremos algoritmos clássicos e implementaremos
um parser/calculadora de expressões completo.

---

## Conceito e Teoria

### O Princípio LIFO

```
  Operações em uma Pilha:

  push(40)          pop()             peek()
  ═══════           ═════             ══════

  ANTES:            ANTES:            Estado:
  ┌─────┐           ┌─────┐           ┌─────┐
  │ 30  │ ← topo    │ 40  │ ← topo    │ 40  │ ← retorna 40
  ├─────┤           ├─────┤           ├─────┤    (sem remover)
  │ 20  │           │ 30  │           │ 30  │
  ├─────┤           ├─────┤           ├─────┤
  │ 10  │           │ 20  │           │ 20  │
  ├─────┤           ├─────┤           ├─────┤
  │     │           │ 10  │           │ 10  │
  └─────┘           └─────┘           └─────┘

  DEPOIS:           DEPOIS:
  ┌─────┐           ┌─────┐
  │ 40  │ ← topo    │ 30  │ ← topo    retorna: Some(40)
  ├─────┤           ├─────┤
  │ 30  │           │ 20  │
  ├─────┤           ├─────┤
  │ 20  │           │ 10  │
  ├─────┤           └─────┘
  │ 10  │
  └─────┘
```

### Pilha na Memória (usando Vec)

```
  Pilha<i32> com 4 elementos:

  Stack (metadados do Vec)        Heap (dados reais)
  ┌──────────────────┐           ┌─────┬─────┬─────┬─────┬─────┬─────┐
  │ ptr ─────────────┼──────────►│ 10  │ 20  │ 30  │ 40  │     │     │
  │ len: 4           │           │     │     │     │topo │livre│livre│
  │ capacity: 6      │           └─────┴─────┴─────┴─────┴─────┴─────┘
  └──────────────────┘                                ▲
                                            push e pop operam aqui
                                            (final do Vec = topo da pilha)
```

### A Call Stack (Pilha de Chamadas)

Toda vez que uma função é chamada, o computador empilha um **stack frame** na call stack:

```
  fn main() {             Execução de main():
      let x = fatorial(4);
  }                       Call Stack:
                          ┌─────────────────┐
  fn fatorial(n: u64)     │ fatorial(1) = 1 │ ← topo (retorna 1)
      -> u64 {            ├─────────────────┤
      if n <= 1 {         │ fatorial(2)     │    (espera resultado)
          1               ├─────────────────┤
      } else {            │ fatorial(3)     │    (espera resultado)
          n * fatorial    ├─────────────────┤
              (n - 1)     │ fatorial(4)     │    (espera resultado)
      }                   ├─────────────────┤
  }                       │ main()          │    (espera resultado)
                          └─────────────────┘

  Desempilhando:
  fatorial(1) retorna 1
  fatorial(2) retorna 2 * 1 = 2
  fatorial(3) retorna 3 * 2 = 6
  fatorial(4) retorna 4 * 6 = 24
  main() recebe 24
```

---

## Complexidade

| Operação          | Complexidade | Descrição                                   |
|-------------------|--------------|---------------------------------------------|
| `push(valor)`    | O(1)*        | Empilha um elemento no topo                  |
| `pop()`          | O(1)         | Desempilha e retorna o topo                  |
| `peek()` / `topo()` | O(1)      | Consulta o topo sem remover                  |
| `esta_vazia()`   | O(1)         | Verifica se a pilha está vazia               |
| `tamanho()`      | O(1)         | Retorna o número de elementos                |
| Busca             | O(n)         | Pilhas não são feitas para busca             |
| Acesso ao i-ésimo | O(n)        | Pilhas não suportam acesso aleatório         |

> *O(1) amortizado. No pior caso, quando o Vec precisa realocar, é O(n).

---

## Implementação em Rust

### Pilha Genérica com Vec

```rust
use std::fmt;

/// Pilha genérica implementada com Vec como armazenamento interno
#[derive(Debug, Clone)]
pub struct Pilha<T> {
    elementos: Vec<T>,
}

impl<T> Pilha<T> {
    /// Cria uma pilha vazia
    pub fn nova() -> Self {
        Pilha {
            elementos: Vec::new(),
        }
    }

    /// Cria uma pilha com capacidade pré-alocada
    pub fn com_capacidade(capacidade: usize) -> Self {
        Pilha {
            elementos: Vec::with_capacity(capacidade),
        }
    }

    /// Empilha um elemento no topo — O(1) amortizado
    pub fn empilhar(&mut self, valor: T) {
        self.elementos.push(valor);
    }

    /// Desempilha e retorna o elemento do topo — O(1)
    pub fn desempilhar(&mut self) -> Option<T> {
        self.elementos.pop()
    }

    /// Retorna referência ao topo sem remover — O(1)
    pub fn topo(&self) -> Option<&T> {
        self.elementos.last()
    }

    /// Retorna referência mutável ao topo sem remover — O(1)
    pub fn topo_mut(&mut self) -> Option<&mut T> {
        self.elementos.last_mut()
    }

    /// Verifica se a pilha está vazia — O(1)
    pub fn esta_vazia(&self) -> bool {
        self.elementos.is_empty()
    }

    /// Retorna o número de elementos — O(1)
    pub fn tamanho(&self) -> usize {
        self.elementos.len()
    }

    /// Remove todos os elementos da pilha
    pub fn limpar(&mut self) {
        self.elementos.clear();
    }
}

/// Exibição formatada da pilha
impl<T: fmt::Display> fmt::Display for Pilha<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "┌─── Topo ───┐")?;
        for elem in self.elementos.iter().rev() {
            writeln!(f, "│ {:^10} │", elem)?;
        }
        writeln!(f, "└────────────┘")?;
        Ok(())
    }
}

fn main() {
    let mut pilha: Pilha<i32> = Pilha::nova();

    // Empilhando elementos
    pilha.empilhar(10);
    pilha.empilhar(20);
    pilha.empilhar(30);
    pilha.empilhar(40);

    println!("Pilha após empilhar 10, 20, 30, 40:");
    println!("{}", pilha);

    // Consultando o topo
    if let Some(topo) = pilha.topo() {
        println!("Topo: {}", topo);
    }

    // Desempilhando
    while let Some(valor) = pilha.desempilhar() {
        println!("Desempilhado: {}", valor);
    }

    println!("Pilha vazia? {}", pilha.esta_vazia());
}
```

### Algoritmo: Balanceamento de Parênteses

Um dos usos mais clássicos de pilhas é verificar se uma expressão tem parênteses balanceados:

```rust
/// Verifica se os delimitadores de uma expressão estão balanceados
///
/// Suporta: (), [], {}
/// Exemplos:
///   "(a + b) * [c - d]"    → true
///   "((a + b)"             → false
///   "{[()]}"               → true
///   "([)]"                 → false
fn delimitadores_balanceados(expressao: &str) -> bool {
    let mut pilha: Vec<char> = Vec::new();

    for caractere in expressao.chars() {
        match caractere {
            // Delimitadores de abertura: empilha
            '(' | '[' | '{' => pilha.push(caractere),

            // Delimitadores de fechamento: verifica correspondência
            ')' => {
                if pilha.pop() != Some('(') {
                    return false;
                }
            }
            ']' => {
                if pilha.pop() != Some('[') {
                    return false;
                }
            }
            '}' => {
                if pilha.pop() != Some('{') {
                    return false;
                }
            }

            // Outros caracteres são ignorados
            _ => {}
        }
    }

    // A expressão é balanceada se a pilha estiver vazia
    pilha.is_empty()
}

fn main() {
    let testes = vec![
        ("(a + b) * [c - d]", true),
        ("((a + b)", false),
        ("{[()]}", true),
        ("([)]", false),
        ("", true),
        ("}{", false),
        ("fn main() { let v = vec![1, 2, 3]; }", true),
    ];

    println!("═══ Teste de Balanceamento de Delimitadores ═══\n");
    for (expressao, esperado) in testes {
        let resultado = delimitadores_balanceados(expressao);
        let status = if resultado == esperado { "OK" } else { "FALHA" };
        println!(
            "  [{}] \"{}\" → {} (esperado: {})",
            status, expressao, resultado, esperado
        );
    }
}
```

### Algoritmo: Avaliação de Expressão Pós-fixa (Notação Polonesa Reversa)

```rust
/// Avalia uma expressão em notação pós-fixa (RPN)
///
/// Na notação pós-fixa, os operadores vêm após os operandos:
///   Infixa: (3 + 4) * 2  →  Pós-fixa: 3 4 + 2 *
///
/// O algoritmo usa uma pilha:
///   - Número: empilha
///   - Operador: desempilha dois, aplica, empilha resultado
fn avaliar_posfixa(expressao: &str) -> Result<f64, String> {
    let mut pilha: Vec<f64> = Vec::new();

    for token in expressao.split_whitespace() {
        match token {
            "+" | "-" | "*" | "/" | "^" => {
                // Operador: precisa de dois operandos
                let b = pilha.pop().ok_or("Expressão inválida: faltam operandos")?;
                let a = pilha.pop().ok_or("Expressão inválida: faltam operandos")?;

                let resultado = match token {
                    "+" => a + b,
                    "-" => a - b,
                    "*" => a * b,
                    "/" => {
                        if b == 0.0 {
                            return Err("Erro: divisão por zero".to_string());
                        }
                        a / b
                    }
                    "^" => a.powf(b),
                    _ => unreachable!(),
                };

                pilha.push(resultado);
            }
            // Número: empilha
            _ => {
                let numero: f64 = token
                    .parse()
                    .map_err(|_| format!("Token inválido: '{}'", token))?;
                pilha.push(numero);
            }
        }
    }

    // Deve sobrar exatamente um valor na pilha
    if pilha.len() != 1 {
        return Err(format!(
            "Expressão inválida: {} valores restantes na pilha",
            pilha.len()
        ));
    }

    Ok(pilha.pop().unwrap())
}

fn main() {
    let expressoes = vec![
        ("3 4 +", "(3 + 4) = 7"),
        ("3 4 + 2 *", "(3 + 4) * 2 = 14"),
        ("5 1 2 + 4 * + 3 -", "5 + (1 + 2) * 4 - 3 = 14"),
        ("2 3 ^ 1 -", "2^3 - 1 = 7"),
        ("10 2 /", "10 / 2 = 5"),
        ("15 7 1 1 + - / 3 * 2 1 1 + + -", "expressão complexa"),
    ];

    println!("═══ Avaliação de Expressões Pós-fixas ═══\n");
    for (expr, descricao) in expressoes {
        match avaliar_posfixa(expr) {
            Ok(resultado) => {
                println!("  \"{}\"", expr);
                println!("  {} → {:.2}\n", descricao, resultado);
            }
            Err(erro) => {
                println!("  \"{}\" → ERRO: {}\n", expr, erro);
            }
        }
    }
}
```

---

## Métodos Principais

| Método                  | Tipo Retorno    | Descrição                                 |
|-------------------------|-----------------|-------------------------------------------|
| `Pilha::nova()`        | `Pilha<T>`      | Cria uma pilha vazia                       |
| `com_capacidade(n)`    | `Pilha<T>`      | Cria com capacidade pré-alocada            |
| `empilhar(valor)`      | `()`            | Adiciona ao topo — O(1) amortizado         |
| `desempilhar()`        | `Option<T>`     | Remove e retorna o topo — O(1)             |
| `topo()`               | `Option<&T>`    | Referência ao topo sem remover — O(1)      |
| `topo_mut()`           | `Option<&mut T>` | Referência mutável ao topo — O(1)         |
| `esta_vazia()`         | `bool`          | Verifica se está vazia — O(1)              |
| `tamanho()`            | `usize`         | Número de elementos — O(1)                 |
| `limpar()`             | `()`            | Remove todos os elementos                  |

### Usando Vec Diretamente como Pilha

Na prática, muitos desenvolvedores Rust simplesmente usam `Vec<T>` como pilha:

```rust
fn exemplo_vec_como_pilha() {
    let mut pilha: Vec<String> = Vec::new();

    // push = empilhar
    pilha.push("primeiro".to_string());
    pilha.push("segundo".to_string());
    pilha.push("terceiro".to_string());

    // pop = desempilhar
    while let Some(valor) = pilha.pop() {
        println!("{}", valor);
        // Imprime: terceiro, segundo, primeiro (LIFO)
    }

    // last = topo (peek)
    pilha.push("único".to_string());
    if let Some(topo) = pilha.last() {
        println!("Topo: {}", topo);
    }
}
```

---

## Exemplo Prático: Parser e Calculadora de Expressões Infixas

Um exemplo realista e completo: converter uma expressão infixa (como "3 + 4 * 2") para pós-fixa
usando o algoritmo Shunting-Yard de Dijkstra, e então avaliar o resultado.

```rust
use std::fmt;

/// Tipos de token reconhecidos pelo parser
#[derive(Debug, Clone, PartialEq)]
enum Token {
    Numero(f64),
    Operador(char),
    ParenEsquerdo,
    ParenDireito,
}

impl fmt::Display for Token {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Token::Numero(n) => write!(f, "{}", n),
            Token::Operador(op) => write!(f, "{}", op),
            Token::ParenEsquerdo => write!(f, "("),
            Token::ParenDireito => write!(f, ")"),
        }
    }
}

/// Retorna a precedência do operador (maior = mais prioritário)
fn precedencia(op: char) -> u8 {
    match op {
        '+' | '-' => 1,
        '*' | '/' => 2,
        '^' => 3,
        _ => 0,
    }
}

/// Verifica se o operador é associativo à direita
fn associativo_direita(op: char) -> bool {
    op == '^'
}

/// Tokeniza uma expressão em string para uma lista de tokens
fn tokenizar(expressao: &str) -> Result<Vec<Token>, String> {
    let mut tokens = Vec::new();
    let mut chars = expressao.chars().peekable();

    while let Some(&c) = chars.peek() {
        match c {
            // Espaços em branco: ignorar
            ' ' | '\t' => {
                chars.next();
            }
            // Dígitos e ponto decimal: construir número
            '0'..='9' | '.' => {
                let mut numero_str = String::new();
                while let Some(&d) = chars.peek() {
                    if d.is_ascii_digit() || d == '.' {
                        numero_str.push(d);
                        chars.next();
                    } else {
                        break;
                    }
                }
                let numero: f64 = numero_str
                    .parse()
                    .map_err(|_| format!("Número inválido: '{}'", numero_str))?;
                tokens.push(Token::Numero(numero));
            }
            // Operadores
            '+' | '-' | '*' | '/' | '^' => {
                tokens.push(Token::Operador(c));
                chars.next();
            }
            // Parênteses
            '(' => {
                tokens.push(Token::ParenEsquerdo);
                chars.next();
            }
            ')' => {
                tokens.push(Token::ParenDireito);
                chars.next();
            }
            // Caractere desconhecido
            _ => {
                return Err(format!("Caractere inesperado: '{}'", c));
            }
        }
    }

    Ok(tokens)
}

/// Algoritmo Shunting-Yard: converte infixa para pós-fixa
///
/// Usa duas estruturas:
///   - Fila de saída (resultado em pós-fixa)
///   - Pilha de operadores
fn shunting_yard(tokens: &[Token]) -> Result<Vec<Token>, String> {
    let mut saida: Vec<Token> = Vec::new();
    let mut pilha_operadores: Vec<Token> = Vec::new();

    for token in tokens {
        match token {
            Token::Numero(_) => {
                // Números vão direto para a saída
                saida.push(token.clone());
            }
            Token::Operador(op) => {
                // Desempilha operadores de maior ou igual precedência
                while let Some(topo) = pilha_operadores.last() {
                    match topo {
                        Token::Operador(op_topo) => {
                            let prec_topo = precedencia(*op_topo);
                            let prec_atual = precedencia(*op);

                            let deve_desempilhar = if associativo_direita(*op) {
                                prec_topo > prec_atual
                            } else {
                                prec_topo >= prec_atual
                            };

                            if deve_desempilhar {
                                saida.push(pilha_operadores.pop().unwrap());
                            } else {
                                break;
                            }
                        }
                        _ => break,
                    }
                }
                pilha_operadores.push(token.clone());
            }
            Token::ParenEsquerdo => {
                pilha_operadores.push(token.clone());
            }
            Token::ParenDireito => {
                // Desempilha até encontrar o parêntese esquerdo
                let mut encontrou = false;
                while let Some(topo) = pilha_operadores.pop() {
                    if topo == Token::ParenEsquerdo {
                        encontrou = true;
                        break;
                    }
                    saida.push(topo);
                }
                if !encontrou {
                    return Err("Parêntese direito sem correspondente".to_string());
                }
            }
        }
    }

    // Desempilha operadores restantes
    while let Some(op) = pilha_operadores.pop() {
        if op == Token::ParenEsquerdo {
            return Err("Parêntese esquerdo sem correspondente".to_string());
        }
        saida.push(op);
    }

    Ok(saida)
}

/// Avalia uma expressão pós-fixa tokenizada
fn avaliar_tokens_posfixa(tokens: &[Token]) -> Result<f64, String> {
    let mut pilha: Vec<f64> = Vec::new();

    for token in tokens {
        match token {
            Token::Numero(n) => pilha.push(*n),
            Token::Operador(op) => {
                let b = pilha.pop().ok_or("Faltam operandos")?;
                let a = pilha.pop().ok_or("Faltam operandos")?;

                let resultado = match op {
                    '+' => a + b,
                    '-' => a - b,
                    '*' => a * b,
                    '/' => {
                        if b == 0.0 {
                            return Err("Divisão por zero".to_string());
                        }
                        a / b
                    }
                    '^' => a.powf(b),
                    _ => return Err(format!("Operador desconhecido: {}", op)),
                };
                pilha.push(resultado);
            }
            _ => return Err("Token inesperado na expressão pós-fixa".to_string()),
        }
    }

    if pilha.len() != 1 {
        return Err("Expressão malformada".to_string());
    }

    Ok(pilha.pop().unwrap())
}

/// Calcula uma expressão infixa completa
fn calcular(expressao: &str) -> Result<f64, String> {
    let tokens = tokenizar(expressao)?;
    let posfixa = shunting_yard(&tokens)?;
    avaliar_tokens_posfixa(&posfixa)
}

fn main() {
    let expressoes = vec![
        "3 + 4 * 2",
        "(3 + 4) * 2",
        "2 ^ 3 ^ 2",
        "10 / (5 - 3)",
        "3.14 * 2.5 ^ 2",
        "(1 + 2) * (3 + 4) / (5 - 2)",
        "100 - 50 * 2 + 25",
    ];

    println!("════════════════════════════════════════════");
    println!("       CALCULADORA DE EXPRESSÕES            ");
    println!("════════════════════════════════════════════\n");

    for expr in expressoes {
        match calcular(expr) {
            Ok(resultado) => {
                // Mostra também a forma pós-fixa
                let tokens = tokenizar(expr).unwrap();
                let posfixa = shunting_yard(&tokens).unwrap();
                let posfixa_str: Vec<String> = posfixa.iter().map(|t| t.to_string()).collect();

                println!("  Infixa:   {}", expr);
                println!("  Pós-fixa: {}", posfixa_str.join(" "));
                println!("  Resultado: {:.4}\n", resultado);
            }
            Err(erro) => {
                println!("  {} → ERRO: {}\n", expr, erro);
            }
        }
    }
}
```

---

## Comparação com Outras Estruturas

| Critério                    | Pilha (Vec)     | Fila (VecDeque)  | Lista Ligada    |
|-----------------------------|-----------------|------------------|-----------------|
| Inserção (principal)       | O(1) topo       | O(1) final       | O(1) início     |
| Remoção (principal)        | O(1) topo       | O(1) início      | O(1) início     |
| Acesso ao próximo elemento | O(1) topo       | O(1) frente      | O(1) cabeça     |
| Acesso aleatório           | O(1)*           | O(1)             | O(n)            |
| Semântica                  | LIFO            | FIFO             | Variável        |
| Cache-friendly             | Sim             | Sim              | Não             |

> *O acesso aleatório é possível tecnicamente (por estar em Vec), mas viola a semântica da pilha.

### Quando Usar Pilha?

- **Inversão de ordem**: quando precisa processar elementos na ordem reversa da inserção.
- **Parsing e avaliação de expressões**: compiladores, calculadoras, validação de sintaxe.
- **Backtracking**: algoritmos que precisam "voltar atrás", como resolução de labirintos e DFS.
- **Gerenciamento de estados**: undo/redo, navegação (voltar/avançar).
- **Conversão de recursão para iteração**: qualquer algoritmo recursivo pode ser convertido para
  iterativo usando uma pilha explícita.

### Pilha vs Recursão

Muitas vezes a escolha entre usar uma pilha explícita e usar recursão depende do contexto:

| Aspecto            | Recursão              | Pilha Explícita       |
|--------------------|-----------------------|-----------------------|
| Legibilidade       | Geralmente melhor     | Pode ser mais verbosa |
| Risco de overflow  | Sim (stack limitada)  | Não (heap)            |
| Controle           | Menos                 | Total                 |
| Performance        | Overhead de chamadas  | Mais eficiente        |

---

## Exercícios

### Exercício 1: Inverter uma String com Pilha

Implemente uma função `inverter_string(s: &str) -> String` que usa uma pilha para inverter
uma string. Considere caracteres Unicode (como acentos e emojis).

**Dica:** Empilhe os caracteres e depois desempilhe para construir a string invertida.
Compare com o método `chars().rev().collect()` — qual é mais idiomático em Rust?

### Exercício 2: Converter Infixa para Pós-fixa

Estenda o parser do exemplo prático para suportar:
- Números negativos (ex: "-5 + 3")
- Funções unárias como `sqrt` e `abs`
- Variáveis (ex: "x + 2", onde x é fornecido em um mapa)

**Dica:** Para números negativos, você pode tratar o `-` unário como um operador especial
durante a tokenização.

### Exercício 3: Verificador de Código HTML

Implemente uma função `html_balanceado(html: &str) -> bool` que verifica se as tags HTML
de um trecho estão corretamente aninhadas. Considere tags como `<div>`, `</div>`, `<p>`,
`</p>`, `<span>`, `</span>`. Tags auto-fechantes como `<br/>` e `<img/>` podem ser
ignoradas.

**Dica:** Empilhe os nomes das tags de abertura. Ao encontrar uma tag de fechamento, verifique
se corresponde ao topo da pilha.

---

## Conclusão

A pilha é uma estrutura deceptivamente simples que sustenta alguns dos algoritmos mais
importantes da computação. Em Rust, sua implementação é elegante graças ao `Vec<T>`, que
já oferece operações LIFO eficientes nativamente.

**Pontos-chave a lembrar:**

1. **LIFO** — o último a entrar é o primeiro a sair. Essa propriedade é a essência da pilha.

2. **Todas as operações principais são O(1)** — empilhar, desempilhar e consultar o topo.

3. **Na prática, use `Vec<T>` diretamente** como pilha — não precisa de wrapper na maioria
   dos casos.

4. **O algoritmo Shunting-Yard** demonstra o poder das pilhas: com apenas uma pilha auxiliar,
   é possível converter e avaliar expressões matemáticas completas.

5. **A call stack** é uma pilha — entender pilhas é entender como o próprio programa executa.

6. **Pilhas explícitas** podem substituir recursão, evitando stack overflow em entradas grandes.

Dominar a pilha é essencial para qualquer programador, e entendê-la em Rust reforça conceitos
fundamentais de ownership e gerenciamento de memória.
