Skip to content

03 · Variables and Bindings

Bindings: let, let mut and var

Creating immutable bindings with let, mutable ones with let mut, and the var shorthand.

On this page

In Zolo, every binding starts with let and is immutable by default — the compiler prevents reassignment, eliminating a whole class of aliasing bugs at zero runtime cost:

let creates an immutable binding; the type is inferred from the initial expression.

01-let-immutable.zolo
Playground
// Feature: Immutable binding with `let`
// Syntax: `let name = expression`
// When to use: the default case — every value that does NOT need to
// be reassigned. Immutability by default avoids aliasing bugs.

// `let` creates an immutable binding; the type is inferred.
let name = "Alice"
let age = 30
let pi = 3.14159
let active = true

print(name)  // Alice
print(age)  // 30
print(pi)  // 3.14159
print(active)  // true

// Reassigning a `let` is a compile error (uncomment to test):
// name = "Bob"  // error: cannot assign to immutable binding

// Immutable bindings accept any expression as their initial value.
let total = 10 + 20 * 2
let greeting = "Hello, " + name + "!"
print(total)  // 50
print(greeting)  // Hello, Alice!

When a value needs to change over time — a counter, an accumulator — add mut to the declaration:

let mut allows reassignment and compound-assignment operators (+=, *=, -=).

02-let-mut.zolo
Playground
// Feature: Mutable binding with `let mut`
// Syntax: `let mut name = value` — then `name = new_value`
// When to use: counters, accumulators, any state that changes
// inside loops or branches. The `mut` keyword is required — Zolo
// treats mutability as opt-in to keep intent visible.

// Mutable: needs `mut` in the declaration.
let mut counter = 0
counter = counter + 1
counter = counter + 1
counter = counter + 1
print(counter)  // 3

// Classic accumulator in a loop.
let mut sum = 0
for i in 1..=10 {
  sum = sum + i
}
print(sum)  // 55

// Reassignment respects the type inferred at declaration.
let mut score: int = 100
score = 90
score = 80
print(score)  // 80

// Compound assignment — equivalent to `x = x + 5`.
let mut x = 10
x += 5
x *= 2
x -= 1
print(x)  // 29

var is an exact alias for let mut. Use var when you want to save keystrokes or when you plan to use storage classes (var<lazy>, var<persistent>, etc.) — the <...> syntax only exists on var:

var and let mut are interchangeable; only var accepts storage modifiers.

11-var-alias.zolo
Playground
// Feature: `var` — alias for `let mut`
// Syntax: `var name = value` — then `name = new_value`
// When to use: anywhere `let mut` is valid. Same semantics, fewer
// keystrokes, and the gateway to storage classes (`var<...>`).
// `let mut` continues to work; both are accepted indefinitely.

// Mutable binding via the new shorter form.
var counter = 0
counter = counter + 1
counter = counter + 1
counter = counter + 1
print(counter)  // 3

// Equivalent in every way to `let mut counter = 0`.
let mut also_counter = 0
also_counter = also_counter + 1
print(also_counter)  // 1

// Compound assignment works the same.
var score = 100
score += 10
score -= 5
print(score)  // 105

// Type annotations also work.
var ratio: float = 0.5
ratio = ratio * 2
print(ratio)  // 1.0

// Why prefer `var`? Storage classes use the `var<...>` syntax —
// see 12-var-lazy.zolo, 13-var-persistent.zolo, etc. `let mut` does
// not accept storage classes (would be ambiguous to parse).

Challenge

Declare an accumulator var total = 0 and add the numbers from 1 to 100 with a for loop. Confirm that the result is 5050.

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

enespt-br