Control Flow in Lua
Learn conditionals, loops, and loop control in Lua—if/elseif/else, while, repeat-until, numeric and generic for, break, and the goto idiom—with Docker-ready examples
Control flow is how a program decides what to run and how many times to run it. As a dynamically typed scripting language, Lua keeps its control structures small and consistent: a handful of keywords cover every branch and loop you will need, and each block is closed with an explicit end (or until) rather than relying on indentation or braces.
Two things make Lua’s control flow distinctive. First, its notion of truthiness is unusually strict: only nil and false are falsy. Every other value—including 0, the empty string "", and empty tables {}—is truthy. This trips up programmers coming from C, Python, or JavaScript, where 0 and "" are falsy. Second, Lua deliberately omits a few features other languages have: there is no ternary operator and no continue statement. Instead, Lua provides idioms—the and/or short-circuit trick and the goto label—that achieve the same results.
In this tutorial you will learn conditionals (if/elseif/else), all four of Lua’s loops (while, repeat-until, numeric for, and generic for), and loop control with break and goto. Every example is self-contained and runnable in Docker.
Conditionals: if / elseif / else
Lua’s conditional uses if ... then, optional elseif ... then branches, an optional else, and a closing end. Note that it is elseif (one word), not else if.
Create a file named conditionals.lua:
| |
The cond and a or b idiom works because and/or short-circuit and return one of their operands (not a boolean): and returns its second operand when the first is truthy, and or returns its second operand when the first is falsy. The one caveat: it breaks if a itself can be nil or false, in which case the or b branch fires unexpectedly.
Loops: while, repeat-until, and numeric for
Lua offers four loops. The while loop tests its condition before each iteration; repeat-until tests after, so its body always runs at least once. The numeric for counts from a start value to a stop value with an optional step.
Create a file named loops.lua:
| |
Here io.write prints without a trailing newline (unlike print), so the bare print() calls add the line breaks. Remember Lua arrays are 1-indexed: ipairs starts counting at 1, not 0.
Loop control: break and the goto idiom
break exits the innermost loop immediately. Lua has no continue keyword; the idiomatic replacement is a goto jump to a ::continue:: label placed at the end of the loop body.
Create a file named loop_control.lua:
| |
In the odd-numbers loop, even values jump straight to ::continue:: (the last statement in the body) and skip the io.write. In the nested loop, break only ends the inner b loop—the outer a loop keeps going.
Stable iteration with pairs
The generic for also works with pairs, which iterates over all keys of a table, including string keys. Important: pairs does not guarantee any particular order. When you need a predictable order, collect the keys into an array and sort them.
Create a file named iterate_table.lua:
| |
The #subjects + 1 expression uses the length operator # to append to the end of the array. The underscore _ is a conventional name for a loop variable you do not intend to use (here, the numeric index from ipairs).
Running with Docker
| |
Expected Output
Running conditionals.lua:
It's mild
0 is truthy in Lua
name is not set
Working age
Status: adult
Running loops.lua:
Numeric for loop:
1 2 3 4 5
Countdown:
3 2 1
While loop (powers of two):
1 2 4 8 16
Repeat-until:
1 2 3
ipairs over an array:
1: apple
2: banana
3: cherry
Running loop_control.lua:
Finding first multiple of 7:
Found: 7
Odd numbers from 1 to 10:
1 3 5 7 9
Products, skipping any that exceed 6:
1x1=1 1x2=2 1x3=3 2x1=2 2x2=4 2x3=6 3x1=3 3x2=6
Running iterate_table.lua:
history = 78
math = 90
science = 85
Key Concepts
- Only
nilandfalseare falsy. Everything else—0,"",{}—is truthy. This is one of Lua’s biggest gotchas for newcomers. - Blocks close with
end.if,while,for, and function bodies all end withend; therepeatloop closes withuntil <condition>instead. - It’s
elseif, one word. Writingelse ifopens a nestedifthat needs its ownend. - No ternary operator. Use the
cond and a or bshort-circuit idiom—but beware whenacan benilorfalse. - No
continuekeyword. Jump to a::continue::label withgototo skip to the next iteration. - Four loops, distinct uses.
whiletests before,repeat-untiltests after (runs at least once), numericforcounts a range, genericforwalks a table. repeat-untilchecks an exit condition. The loop stops when the condition becomes true—the opposite sense ofwhile.ipairsis ordered,pairsis not. Useipairsfor arrays (stopping at the firstnil); usepairsfor all keys, sorting first if you need a stable order. Remember arrays are 1-indexed.
Running Today
All examples can be run using Docker:
docker pull nickblah/lua:5.4-alpine
Comments
Loading comments...
Leave a Comment