Capítulo 04: Funções, Expressões vs Declarações e Escopo Léxico
Capítulo 04: Funções, Expressões vs Declarações e Escopo Léxico
Especialização em Sistemas com Rust • Rust 2021 Edition • Ownership, Concorrência Segura e Sistemas de Alta Performance
🗺️ Mapa Conceitual de Avaliação de Expressões e Quadros de Pilha (Stack Frames)
flowchart TD
Execucao["Execução Sequencial do Programa"] --> Chamada["Invocação de Função: fn calcular(x, y) -> T"]
subgraph STACK_FRAME["Quadro de Pilha da Função (Stack Frame)"]
Params["Parâmetros Locais (Copiados ou Movidos na Stack)"]
Statements["Declarações (Statements com ';'): Avaliam para o Tipo Unit ()"]
BlockExpr["Blocos de Escopo Intermediários: { let temp = ...; temp * 2 }"]
FinalExpr["Expressão de Retorno Implícita (Sem ';'): Produz o Valor de Saída"]
end
Chamada --> STACK_FRAME
Statements --> BlockExpr --> FinalExpr
FinalExpr --> Retorno["Valor Retornado ao Chamador"]
Retorno --> Desalocacao["Desalocação Instantânea do Stack Frame (SP Pointer Reset)"]
style Execucao fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style Chamada fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style STACK_FRAME fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style Desalocacao fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
🏛️ 1. O Paradigma de Expressões e Declarações
No Rust, a distinção entre Declarações (Statements) e Expressões (Expressions) é rigorosamente definida pelo compilador e dita a forma como o código é estruturado:
- Statements (Declarações): São instruções imperativas que executam uma ação mas não retornam um valor. Em Rust, declarações sempre terminam com ponto e vírgula (
;). Exemplos:let x = 6;ou chamadas de função com ponto e vírgula no final. O valor avaliado de qualquer declaração é o tipo Unit(). - Expressions (Expressões): Avaliam para um resultado concreto e não possuem ponto e vírgula no final. Em Rust, quase todas as construções (operações matemáticas, chamadas de função, blocos de código
{ ... },if/else,match) são expressões.
⚙️ 2. Anatomia de Funções Idiomáticas em Rust
As funções em Rust são declaradas com a palavra-chave fn. Os parâmetros devem sempre ter seus tipos explicitamente anotados, assim como o tipo de retorno quando diferente de ():
// Parâmetros tipados e retorno formal com seta '-> Tipo'
fn calcular_vazao_litros_segundo(diametro_mm: f64, velocidade_m_s: f64) -> f64 {
let raio_m = (diametro_mm / 1000.0) / 2.0;
let area_m2 = std::f64::consts::PI * raio_m * raio_m;
// RETORNO IMPLÍCITO:
// Esta linha NÃO tem ponto e vírgula! O valor da expressão é retornado.
area_m2 * velocidade_m_s * 1000.0
}
2.1 Por que o Ponto e Vírgula Faz Diferença?
Colocar um ponto e vírgula acidental na última linha de uma função transforma a expressão em um statement, fazendo a função retornar o tipo Unit () em vez do valor esperado:
fn soma(a: i32, b: i32) -> i32 {
a + b; // ERRO: mismatched types! Retorna '()' em vez de 'i32'
}
2.2 Retorno Antecipado com return
O retorno implícito na última linha é o padrão idiomático do Rust. No entanto, para validações de guarda (guard clauses) ou condições de saída precoce, a palavra-chave return é perfeitamente válida e recomendada:
fn autenticar_sessao(token: &str) -> bool {
if token.is_empty() {
return false; // Early return imediato
}
token.starts_with("BEARER_AUTH_")
}
📦 3. Blocos de Escopo como Expressões de Primeira Classe
Um par de chaves { ... } cria um novo escopo léxico na memória e, simultaneamente, atua como uma expressão avaliada:
let taxa_servico = 15.0;
let custo_total = {
let preco_base = 120.0;
let desconto = 10.0;
// A última linha do bloco é avaliada e atribuída a 'custo_total'
(preco_base - desconto) + taxa_servico
};
println!("Custo Total Calculado: R$ {:.2}", custo_total);
// preco_base e desconto deixaram de existir aqui!
🧠 4. Escopo Léxico e Gestão na Memória Stack
A memória Stack organiza os dados em quadros de pilha (Stack Frames):
- Ao invocar uma função, um novo quadro é alocado contendo os parâmetros e as variáveis locais da função.
- O acesso a essas variáveis é instantâneo em tempo de execução através do registrador Stack Pointer (SP) da CPU.
- Quando o fluxo de execução atinge o fechamento da chave
}, o Stack Frame é imediatamente desalocado com zero custo computacional de varredura.
⚖️ 5. Comparativo: Statements vs Expressions
| Característica | Statement (Declaração) | Expression (Expressão) |
|---|---|---|
| Termina com Ponto e Vírgula? | SIM (obrigatoriamente ;) |
NÃO (se tiver ;, vira statement) |
| Produz Valor de Saída? | Não (produz o tipo Unit ()) |
SIM (produz o tipo resultante T) |
| Exemplos Canônicos | let a = 5;, println!("ok"); |
5 + 5, { let x = 2; x * 10 }, if c { 1 } else { 2 } |
Pode ser Atribuído a let? |
Não diretamente | SIM (let y = { expr };) |
⚠️ 6. Armadilhas Comuns e Mensagens do Compilador
- Erro de Ponto e Vírgula no Retorno de Função:
error[E0308]: mismatched types --> src/main.rs:2:5 | 1 | fn obter_id() -> u32 { | --- expected `u32` because of return type 2 | 100; | ^ help: remove this semicolonCausa: O ponto e vírgula na linha 2 transformou
100em statement retornando(). Correção: Remova o ponto e vírgula:100. - Tentar Acessar Variável Criada em Bloco Interno:
Variáveis declaradas dentro de um bloco
{ let x = 10; }morrem assim que o bloco fecha. O compilador acusarácannot find value in this scope.
🛡️ 7. Boas Práticas e Idiomatismo Corporativo
- Guards no Topo: Utilize guard clauses com
returnantecipado no topo das funções para rejeitar argumentos inválidos logo no início, mantendo o corpo principal da função sem aninhamentos profundos deif. - Prefira Retorno Implícito: Não use
returnna última linha da função. A comunidade Rust adota o retorno implícito como a forma idiomática padrão.
📋 8. Cheat-Sheet: Resumo Executivo
// Função pura com retorno implícito
fn dobro(n: i32) -> i32 { n * 2 }
// Early return
fn validar(x: i32) -> bool {
if x < 0 { return false; }
true
}
// Bloco de escopo como expressão
let valor = { let temp = 5; temp + 1 }; // valor = 6