Control flow#

Control flow is how the operator’s program decides what runs next. Lua keeps the surface small: if / elseif / else for branching, while / repeat / for for iteration, break / return / goto for jumps. There is no switch and no exception flow; protected calls (pcall) handle errors.

This page is the day-to-day reference for the control-flow constructs. For the values they branch on, see Types. For error / pcall, see Errors.

Conditional#

if / elseif / else evaluates branches top-to-bottom and takes the first whose condition is truthy. Only false and nil are falsy; everything else is truthy.

        flowchart TD
    A([start]) --> B{x > 0?}
    B -->|true| C[positive]
    B -->|false| D{x == 0?}
    D -->|true| E[zero]
    D -->|false| F[negative]
    C --> Z([end])
    E --> Z
    F --> Z
    
if x > 0 then
  positive()
elseif x == 0 then
  zero()
else
  negative()
end

Lua has no ternary operator, but and / or short-circuit to the same effect when the consequent is itself truthy.

local label = (n % 2 == 0) and "even" or "odd"

Loops#

Three loop forms, picked by what the operator is iterating.

While loop#

        flowchart TD
    A([start]) --> B{cond?}
    B -->|true| C[body]
    C --> B
    B -->|false| D([end])
    

Tests the condition first; skips the body when already false.

local i = 1
while i <= 10 do
  print(i)
  i = i + 1
end

Repeat-until#

        flowchart TD
    A([start]) --> B[body]
    B --> C{cond?}
    C -->|false| B
    C -->|true| D([end])
    

Post-test loop. The body runs once before the condition is checked; the loop exits when the condition turns true. Locals declared in the body are in scope inside the until expression, which is rare in other languages and useful for seeded loops.

repeat
  local line = io.read()
  process(line)
until line == "quit"

Numeric for#

Runs the control variable from a start to a stop value in optional steps. The bounds are evaluated once at entry.

Default step of 1.

for i = 1, 10 do print(i) end

Explicit step.

for i = 1, 10, 2 do print(i) end

Negative step for a countdown.

for i = 10, 1, -1 do print(i) end

Generic for#

Drives an iterator function. ipairs walks a sequence (integer keys 1..n); pairs walks every key in any order. Custom iterators are functions that return the next value (or nil to stop).

ipairs walks integer keys 1..n in order.

for i, v in ipairs({"a", "b", "c"}) do
  print(i, v)
end

pairs walks every key in any order.

for k, v in pairs({name = "rk", age = 30}) do
  print(k, v)
end

A custom iterator (here string.gmatch) is just a function that returns the next value or nil to stop.

for word in string.gmatch("one two three", "%a+") do
  print(word)
end

Jumps#

break, return, and goto interrupt the natural top-to-bottom flow.

Keyword

Effect

break

Exit the innermost for, while, or repeat loop.

return

Exit the current function (optionally with values). Must be the last statement in a block; use do return end for an early-return guard.

goto

Jump to a named ::label:: in the same function. Mostly used as a continue substitute.

for _, line in ipairs(lines) do
  if line:sub(1, 1) == "#" then
    goto continue            -- skip comment lines
  end
  process(line)
  ::continue::
end

Lua has no continue keyword. The goto continue pattern above is the standard workaround.

References#