Shadowing e Escopo
Nesta página
Shadowing é redeclarar um nome com um novo let. Cada let cria um binding
completamente novo — o anterior fica inacessível, mas sem precisar de mut. Isso
é útil para pipelines de transformação onde o nome lógico permanece o mesmo, mas
o tipo ou o valor muda em cada etapa:
let value = "42" → let value = 42 → let value = value * 2; cada let cria um binding novo.
// Feature: Shadowing — redeclaring a name with `let`
// Syntax: `let x = ...` followed by another `let x = ...`
// When to use: transform a value in stages while keeping the same
// name (parse → validate → normalize), change a variable's type
// during a pipeline. Unlike `mut`, every `let` creates a NEW
// binding; the previous one becomes inaccessible.
// Shadowing with type change.
let value = "42" // str
let value = 42 // int (new binding)
let value = value * 2 // int = 84
print(value) // 84
// Useful for "cleaning" data in a pipeline.
let raw = " Hello "
let raw = raw.trim() // "Hello"
let raw = raw.to_upper() // "HELLO"
print(raw) // HELLO
// Shadowing inside blocks (scope).
let x = 10
{
let x = 100 // shadows only inside the block
print(x) // 100
}
print(x) // 10 (outer binding intact)
// Difference vs `mut`:
// - `let mut x = 1; x = 2` -> same binding, new value
// - `let x = 1; let x = 2` -> new binding, still immutable
let count = 1
let count = count + 1
let count = count + 1
print(count) // 3 (each `let` creates a fresh binding)
O escopo léxico determina onde um binding pode ser usado. Todo bloco { ... }
abre um novo escopo: bindings declarados dentro dele morrem ao fechar o } e não
vazam para fora. Funções, if, for e while seguem a mesma regra:
Bindings internos não existem fora do bloco; if let restringe o binding ao ramo verdadeiro.
// Feature: Lexical binding scope
// Syntax: bindings live in the `{ ... }` they were declared in
// When to use: to understand when a variable "dies" and where it
// can be used. Zolo uses lexical scoping, like Rust and JS `let`.
// A nested block has its own scope.
let outer = "outside"
{
let inner = "inside"
print(outer) // outside — outer visible
print(inner) // inside
}
// inner does not exist anymore here (uncomment to test):
// print(inner) // error: undefined variable
print(outer) // outside
// Function scope — params and local bindings disappear on return.
fn compute(n: int) -> int {
let doubled = n * 2
let result = doubled + 10
return result
}
print(compute(5)) // 20
// `doubled` and `result` did not leak outside.
// `if`/`for`/`while` introduce a new scope.
var acc = 0
for i in 1..=3 {
let squared = i * i // lives only inside the loop
acc = acc + squared
}
print(acc) // 1 + 4 + 9 = 14
// `if let` bindings only apply inside the true branch.
let value: int? = 42
if let v = value {
print("inside: {v}") // v exists here
}
// v does not exist outside.
Desafio
No exemplo de shadowing, substitua os let sucessivos por um único let mut com
reatribuições. A saída final deve ser a mesma — compare as duas abordagens e
decida qual deixa a intenção mais clara.
Veja também