Coding · Programming languages
Lua tutorial for beginners
Short answer
Lua is a lightweight, embeddable scripting language used in games, Roblox (through its Luau dialect), Neovim, Redis and embedded devices. It has a small syntax and a single data structure, the table, which serves as array, dictionary and object. This page covers installing Lua, variables and types, control flow, functions, tables, metatables, error handling with pcall and a complete example.
What Lua is and what it is used for
Lua is a small, fast, dynamically typed scripting language created in 1993 at PUC-Rio, a university in Brazil. Its whole interpreter is a compact library written in C, which makes it easy to embed inside larger programs. It has one data structure, the table, and very few concepts, so it is quick to learn.
Lua is mostly used as an embedded scripting language: game engines and games (Roblox uses a Lua dialect called Luau; LÖVE and Defold use Lua), the Neovim editor, the Redis database, network tools such as Nginx with OpenResty, and many embedded devices. LuaJIT is a popular just-in-time compiler for Lua 5.1 code.
Install Lua and run Hello, world
Install Lua with your package manager (for example brew install lua or apt install lua5.4) or build it from the source at lua.org. Run lua alone for an interactive prompt.
-- hello.lua
print("Hello, world!")lua hello.lua
# Hello, world!Comments start with --. A block comment is written --[[ ... ]]. Statements do not need semicolons.
Variables and basic types
Variables are global by default. Always declare them with local unless you really mean a global. Lua has eight basic types; the ones you use every day are below.
local name = "Lua"
local year = 1993 -- integer
local pi = 3.14159 -- float
local ok = true
local nothing = nil
print(type(name), type(year), type(nothing)) -- string number nil
print(math.type(year), math.type(pi)) -- integer float
print(name .. " " .. year) -- Lua 1993 (.. joins strings)
print(#name) -- 3 (length)
print(7 / 2, 7 // 2, 7 % 2, 2 ^ 10) -- 3.5 3 1 1024.0
print(string.format("%.2f", pi)) -- 3.14
print(tonumber("42") + 1, tostring(10)) -- 43 10| Type | Example | Notes |
|---|---|---|
| nil | nil | absence of a value; unset variables are nil |
| boolean | true, false | only false and nil are falsy; 0 and "" are truthy |
| number | 42, 3.14 | integer and float subtypes since Lua 5.3 |
| string | "hi", 'hi', [[long]] | immutable byte strings |
| table | {1, 2, x = 3} | the only data structure |
| function | function() end | first-class values |
The not-equal operator is ~=, not !=. Logical operators are the words and, or and not.
Control flow
Blocks end with the keyword end. Lua has if/elseif/else, while, repeat ... until, a numeric for and a generic for that works with iterators.
local score = 72
if score >= 90 then
print("A")
elseif score >= 70 then
print("B") -- B
else
print("C")
end
for i = 1, 3 do -- 1 to 3, inclusive
io.write(i, " ") -- 1 2 3
end
print()
for i = 10, 1, -4 do io.write(i, " ") end -- 10 6 2
print()
local n = 3
while n > 0 do n = n - 1 end
repeat
n = n + 1
until n >= 2
print(n) -- 2There is no continue statement; use goto continue with a ::continue:: label, or restructure the loop with an if.
Functions
Functions are values. They can take a variable number of arguments with ... and return multiple values. Closures capture local variables from the enclosing scope.
local function add(a, b)
return a + b
end
local function minmax(t)
local lo, hi = math.huge, -math.huge
for _, v in ipairs(t) do
lo, hi = math.min(lo, v), math.max(hi, v)
end
return lo, hi -- two return values
end
local function counter()
local count = 0
return function() -- a closure
count = count + 1
return count
end
end
print(add(2, 3)) -- 5
local lo, hi = minmax({4, 9, 1})
print(lo, hi) -- 1 9
local next_id = counter()
print(next_id(), next_id()) -- 1 2
local function sum(...)
local total = 0
for _, v in ipairs({...}) do total = total + v end
return total
end
print(sum(1, 2, 3)) -- 6Tables: arrays, dictionaries and objects
A table is an associative array that can be indexed by any value except nil. The same structure serves as an array, a dictionary, a set, a module or an object. By convention arrays start at index 1.
local fruits = {"apple", "banana"}
table.insert(fruits, "cherry")
print(#fruits, fruits[1]) -- 3 apple
for i, f in ipairs(fruits) do -- array part, in order
print(i, f)
end
local ages = {ann = 31, bo = 25}
ages["cy"] = 40
ages.bo = nil -- remove a key
for k, v in pairs(ages) do -- all keys, order not defined
print(k, v)
end
table.sort(fruits, function(a, b) return a > b end)
print(table.concat(fruits, ", ")) -- cherry, banana, appleMetatables and object-oriented Lua
Metatables are Lua's signature feature. A metatable attached to a table changes how it behaves: __index supplies missing keys (used for inheritance), and fields such as __add, __eq and __tostring overload operators. The colon syntax obj:method() passes the object as the hidden first parameter self.
local Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
return setmetatable({x = x, y = y}, Vector)
end
function Vector:length()
return math.sqrt(self.x ^ 2 + self.y ^ 2)
end
Vector.__add = function(a, b) return Vector.new(a.x + b.x, a.y + b.y) end
Vector.__tostring = function(v) return "(" .. v.x .. ", " .. v.y .. ")" end
local v = Vector.new(3, 4) + Vector.new(0, 0)
print(tostring(v)) -- (3, 4)
print(v:length()) -- 5.0Lua also has coroutines (coroutine.create, coroutine.resume, coroutine.yield), which let a function pause and resume later. Game scripts often use them for step-by-step behavior that spans several frames.
Error handling
Raise an error with error(message). Catch it with pcall (protected call), which returns false plus the error instead of stopping the program. Many library functions return nil and an error message instead of raising an error, as io.open does.
local function divide(a, b)
if b == 0 then
error("division by zero")
end
return a / b
end
local ok, result = pcall(divide, 10, 2)
print(ok, result) -- true 5.0
ok, result = pcall(divide, 1, 0)
print(ok, result) -- false script.lua:3: division by zero
local f, err = io.open("missing.txt", "r")
if not f then print("open failed: " .. err) endThe file name and line number in the error message depend on where the code runs. assert(value, message) raises an error when value is false or nil and is a short way to check results.
A small complete program: word count
This program counts words in a text with a pattern match, sorts them by frequency and prints the top three. It uses string patterns, tables, sorting with a custom comparator and string formatting.
local text = [[
The cat and the dog. The dog sat,
and the cat ran.
]]
local counts = {}
for word in text:lower():gmatch("%a+") do
counts[word] = (counts[word] or 0) + 1
end
local list = {}
for word, n in pairs(counts) do
list[#list + 1] = {word = word, n = n}
end
table.sort(list, function(a, b)
if a.n ~= b.n then return a.n > b.n end
return a.word < b.word
end)
for i = 1, 3 do
print(string.format("%-4s %d", list[i].word, list[i].n))
end
-- the 4
-- and 2
-- cat 2Where to go next
- The Lua Reference Manual at lua.org, short and complete.
- Programming in Lua by Roberto Ierusalimschy, one of Lua's creators; the first edition is free online.
- The documentation of your host environment, such as Neovim's Lua guide, LÖVE or Roblox's Luau docs.
- LuaRocks, the package manager for Lua modules.
FAQ
Why do Lua arrays start at 1?
Lua's designers chose 1-based indexing to match common mathematical and non-programmer conventions. The standard library functions such as ipairs, # and table.insert all assume sequences start at 1.
Is Lua faster than Python?
The standard Lua interpreter is small and generally fast for a dynamic language, and LuaJIT is much faster for many workloads. Actual speed depends on the task, so measure your own case.
What is the difference between pairs and ipairs?
ipairs walks the array part in order from index 1 until the first nil. pairs visits every key in the table in an unspecified order.
What is Luau?
Luau is a language derived from Lua 5.1 and developed by Roblox. It adds gradual typing and performance improvements and is used for Roblox games.
Is Lua good for beginners?
Yes. It has a small, consistent syntax and few concepts, which is why it is popular for game modding and teaching. Its minimal standard library means you rely on the host program or modules for more features.