Saltar al contenido

Shadowing y Alcance

En esta página

El shadowing consiste en redeclarar un nombre con un nuevo let. Cada let crea un binding completamente nuevo — el anterior queda inaccesible, sin necesidad de mut. Es útil en pipelines de transformación donde el nombre lógico permanece igual pero el tipo o el valor cambia en cada paso:

let value = "42" → let value = 42 → let value = value * 2; cada let crea un binding nuevo.

05-shadowing.zolo
Playground
// 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)

El alcance léxico determina dónde se puede usar un binding. Cada bloque { ... } abre un nuevo alcance: los bindings declarados dentro mueren al cerrar } y no se filtran hacia afuera. Las funciones, if, for y while siguen la misma regla:

Los bindings internos no existen fuera del bloque; if let restringe el binding a la rama verdadera.

09-scoping.zolo
Playground
// 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.

Desafío

En el ejemplo de shadowing, reemplaza los let sucesivos por un único let mut con reasignaciones. La salida final debe ser la misma — compara ambos enfoques y decide cuál expresa mejor la intención.

Buscar en Zolo

9 resultados

enespt-br