Lua
Updated: May 22, 2026Categories: Languages, Gaming
Printed from:
Lua Cheatsheet
Language Overview
Lua is a lightweight, high-level programming language designed primarily for embedded scripting. Created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes at the Pontifical Catholic University of Rio de Janeiro, Lua is known for its simplicity, efficiency, and powerful table-based data structure. It is widely used in game development (World of Warcraft, Roblox), game engines (LÖVE, Defold), embedded systems, neovim configuration, and as a configuration language. The current stable release is Lua 5.4 (5.4.7).
Key characteristics:
- Dynamically typed with a distinct integer/float subtype (since 5.3)
- Garbage-collected (generational GC available since 5.4)
- Embeddable in C/C++
- Supports functional programming paradigms
- Lightweight and fast
- Extremely portable (pure ANSI C implementation)
Basic Syntax
lua
123456789101112131415161718192021-- Single line comment
--[[
Multi-line comment
supports multiple lines
]]
-- Print statement
print("Hello, World!")
-- Basic variable declaration
local x = 10
global_var = 20 -- Global variable (avoid when possible)
-- Local constant (Lua 5.4+)
local PI <const> = 3.14159
-- Function definition
local function greet(name)
return "Hello, " .. name .. "!"
end
-- Calling a function
print(greet("Lua Developer"))
Data Types
lua
12345678910111213141516171819-- Primitive Types
local nil_var = nil -- Represents absence of a value
local bool_true = true -- Boolean true
local bool_false = false -- Boolean false
local num_int = 42 -- Integer subtype (since 5.3)
local num_float = 3.14 -- Float subtype
local str_simple = "Lua" -- String (8-bit clean, immutable)
-- Inspect number subtype (5.3+)
print(math.type(42)) -- "integer"
print(math.type(3.14)) -- "float"
-- Complex Types
local table_array = {1, 2, 3} -- Array-like table
local table_dict = {name = "Lua", version = 5.4} -- Dictionary-like table
local function_var = function() end -- Function as first-class value
local thread_var = coroutine.create(function() end) -- Coroutine thread
local userdata_var -- Userdata (from C)
Variables and Constants
lua
12345678910111213141516171819202122232425-- Local variables (recommended)
local x = 10
local y = 20
-- Global variables (use sparingly)
global_value = 30
-- Multiple variable assignment
local a, b, c = 1, 2, 3
-- True constants with <const> attribute (Lua 5.4+)
local PI <const> = 3.14159
local MAX_CONNECTIONS <const> = 100
-- PI = 4 -- Compile-time error: attempt to assign to const variable
-- To-be-closed variables (Lua 5.4+): __close is called when scope ends
local file <close> = io.open("data.txt", "r")
-- file:close() runs automatically at end of block, even on error
-- Scope demonstration
local function scope_demo()
local local_var = "I'm local" -- Function-level local
internal_var = "I'm global" -- Becomes global (avoid)
end
Operators
lua
1234567891011121314151617181920212223242526272829303132333435-- Arithmetic
local sum = 5 + 3 -- Addition
local diff = 10 - 4 -- Subtraction
local prod = 3 * 6 -- Multiplication
local div = 15 / 3 -- Float division (always returns float)
local idiv = 15 // 4 -- Floor (integer) division -> 3 (5.3+)
local mod = 10 % 3 -- Modulo
local pow = 2 ^ 3 -- Exponentiation (always returns float)
-- Bitwise operators (5.3+, operate on integers)
local band = 0xF0 & 0x0F -- AND
local bor = 0xF0 | 0x0F -- OR
local bxor = 0xFF ~ 0x0F -- XOR
local bnot = ~0 -- NOT
local shl = 1 << 4 -- Left shift
local shr = 256 >> 2 -- Right shift
-- Comparison
local is_equal = (5 == 5) -- Equality
local is_not_equal = (5 ~= 6) -- Inequality
local greater = (10 > 5) -- Greater than
local less_or_equal = (3 <= 4) -- Less than or equal
-- Logical
local and_op = true and false -- Logical AND
local or_op = true or false -- Logical OR
local not_op = not true -- Logical NOT
-- Length operator
local n = #"Lua" -- 3
local t = #{10, 20, 30} -- 3
-- String Concatenation
local full_name = "Lua" .. " " .. "Programming"
Control Structures
lua
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556-- If-Else
local age = 20
if age < 18 then
print("Minor")
elseif age >= 18 and age < 65 then
print("Adult")
else
print("Senior")
end
-- For Loops
-- Numeric loop
for i = 1, 5 do
print(i) -- Prints 1, 2, 3, 4, 5
end
-- Reverse loop
for i = 5, 1, -1 do
print(i) -- Prints 5, 4, 3, 2, 1
end
-- Generic loop (iterate over tables)
local fruits = {"apple", "banana", "cherry"}
for index, fruit in ipairs(fruits) do
print(index, fruit)
end
-- While Loop
local count = 0
while count < 5 do
print(count)
count = count + 1
end
-- Repeat-Until Loop
local x = 0
repeat
x = x + 1
print(x)
until x >= 5
-- Break (Lua has no `continue`; use `goto continue` since 5.2)
for i = 1, 10 do
if i == 3 then
break -- Exit loop
end
print(i)
end
-- goto / labels (5.2+) - common idiom for "continue"
for i = 1, 5 do
if i % 2 == 0 then goto continue end
print(i) -- prints odd numbers
::continue::
end
Functions
lua
12345678910111213141516171819202122232425262728293031323334353637383940414243444546-- Simple function (prefer local)
local function add(a, b)
return a + b
end
-- Function with multiple return values
local function get_person_details()
return "John", 30, "Engineer"
end
-- Anonymous/Lambda functions
local multiply = function(a, b) return a * b end
-- Closures
local function counter()
local count = 0
return function()
count = count + 1
return count
end
end
local increment = counter()
print(increment()) -- 1
print(increment()) -- 2
-- Variadic functions
local function sum(...)
local total = 0
for _, v in ipairs({...}) do
total = total + v
end
return total
end
print(sum(1, 2, 3, 4)) -- 10
-- select() and table.pack handle varargs with nils safely
local function count_args(...)
return select("#", ...) -- exact arg count, including nils
end
-- Tail calls are properly optimized (no stack growth)
local function loop(n)
if n == 0 then return "done" end
return loop(n - 1) -- proper tail call
end
Data Structures
lua
123456789101112131415161718192021222324252627282930-- Tables as Arrays
local fruits = {"apple", "banana", "cherry"}
print(fruits[1]) -- "apple" (1-based indexing)
table.insert(fruits, "date")
table.remove(fruits, 2)
-- Move a range of elements (5.3+)
local copy = {}
table.move(fruits, 1, #fruits, 1, copy)
-- Tables as Dictionaries
local person = {
name = "John",
age = 30,
city = "New York"
}
print(person.name)
person.job = "Developer"
-- Mixed Tables
local mixed = {
42,
name = "Mixed Table",
[true] = "Boolean key"
}
-- Pack / unpack varargs (5.2+)
local packed = table.pack("a", "b", "c") -- {n=3, "a", "b", "c"}
print(table.unpack(packed, 1, packed.n))
Object-Oriented Programming
lua
12345678910111213141516171819202122232425262728293031323334353637383940414243-- Metatable-based OOP
local Person = {}
Person.__index = Person
function Person.new(name, age)
local self = setmetatable({}, Person)
self.name = name
self.age = age
return self
end
function Person:introduce()
print("Hi, I'm " .. self.name)
end
local john = Person.new("John", 30)
john:introduce()
-- Inheritance
local Student = setmetatable({}, {__index = Person})
Student.__index = Student
function Student.new(name, age, grade)
local self = Person.new(name, age)
setmetatable(self, Student)
self.grade = grade
return self
end
function Student:study()
print(self.name .. " is studying")
end
local jane = Student.new("Jane", 25, 12)
jane:introduce() -- Inherited method
jane:study() -- Student's method
-- Useful metamethods:
-- __index, __newindex, __call, __tostring, __len,
-- __add/__sub/__mul/__div/__mod/__pow/__unm/__idiv,
-- __band/__bor/__bxor/__bnot/__shl/__shr (5.3+),
-- __eq/__lt/__le, __concat, __gc, __close (5.4+), __name
Error Handling
lua
12345678910111213141516171819202122232425262728293031-- Protected Call
local function divide(a, b)
if b == 0 then
error("Cannot divide by zero", 2) -- level 2 = caller
end
return a / b
end
local ok, result = pcall(divide, 10, 2)
print(ok, result) -- true, 5
local ok, err = pcall(divide, 10, 0)
print(ok, err) -- false, error message
-- xpcall with message handler and forwarded args (5.2+)
local function handler(err)
return debug.traceback("Custom: " .. tostring(err), 2)
end
xpcall(divide, handler, 10, 0)
-- assert raises an error with a message when its first arg is falsy
local f = assert(io.open("config.lua", "r"), "config not found")
-- To-be-closed locals make cleanup automatic on error (5.4+)
do
local f <close> = assert(io.open("data.txt", "r"))
-- file auto-closed even if the next line errors
local data = f:read("a")
end
File I/O
lua
1234567891011121314151617181920212223242526-- Writing to a file
local file = assert(io.open("example.txt", "w"))
file:write("Hello, Lua!")
file:close()
-- Reading entire file (use "a" in 5.3+, "*a" still works for back-compat)
local file = assert(io.open("example.txt", "r"))
local content = file:read("a")
print(content)
file:close()
-- Iterating through file lines
for line in io.lines("example.txt") do
print(line)
end
-- Binary data: pack/unpack (5.3+)
local bytes = string.pack(">I4 I2", 65536, 42)
local a, b = string.unpack(">I4 I2", bytes)
-- Recommended: pair file handles with <close> (5.4+)
do
local f <close> = assert(io.open("example.txt", "r"))
print(f:read("a"))
end
Common Libraries and Frameworks
-
Standard Libraries:
math: Mathematical functions (includesmath.type,math.tointegersince 5.3)string: String manipulation,string.pack/unpack/packsize(5.3+)table: Table operations, includingtable.move,table.pack,table.unpackio: Input/Output operationsos: Operating system interactionscoroutine: Cooperative multitasking (coroutine.closeadded in 5.4)utf8: Basic UTF-8 support (5.3+)debug: Introspection and hooks- Note:
bit32was deprecated in 5.3 and removed in 5.4 — use native bitwise operators
-
Popular External Libraries:
- LuaSocket: Networking
- LuaSec: TLS/SSL support
- LPeg: Pattern matching / parsing (Ierusalimschy)
- Lua Lanes / effil: Multithreading
- busted / LuaUnit: Unit testing
- Penlight: General-purpose utility library
- Lapis / Sailor: Web frameworks (on OpenResty)
- LuaJIT: High-performance, mostly 5.1-compatible JIT runtime
- Teal / Luau: Statically-typed dialects compiling to Lua
Best Practices
- Prefer local variables (faster lookup, smaller scope)
- Use the
<const>attribute for true constants (5.4+) - Use
<close>for deterministic resource cleanup (5.4+) - Use meaningful variable names
- Leverage metatables for advanced programming
- Be mindful of memory usage; consider the generational GC (
collectgarbage("generational")) - Use
require()for modular code; return a table from each module - Avoid global variables (consider
local _ENV = {}for module-local globals) - Use
pairs()for hash-like tables,ipairs()for sequences - Remember 1-based indexing and that
#tis only well-defined for sequences - Use
//for integer division and/only when you want a float - Use coroutines for cooperative multitasking and iterators
- Profile with
os.clock()or thedebughooks before optimizing
Testing
lua
1234567891011121314151617181920-- Using LuaUnit
local lu = require("luaunit")
function test_addition()
lu.assertEquals(1 + 1, 2)
end
function test_string_concat()
lu.assertEquals("Hello" .. " World", "Hello World")
end
os.exit(lu.LuaUnit.run())
-- Or with busted (BDD style)
-- describe("math", function()
-- it("adds numbers", function()
-- assert.are.equal(4, 2 + 2)
-- end)
-- end)
Resources for Further Learning
- Official Lua Documentation: https://www.lua.org/docs.html
- Lua 5.4 Reference Manual: https://www.lua.org/manual/5.4/
- Programming in Lua, 4th edition (covers 5.3) by Roberto Ierusalimschy
- Lua Users Wiki: http://lua-users.org/wiki/
- LÖVE Game Framework: https://love2d.org/
- LuaRocks Package Manager: https://luarocks.org/
- LuaJIT: https://luajit.org/
- Awesome Lua: https://github.com/LewisJEllis/awesome-lua
Tips and Tricks
- Use
collectgarbage("generational")or"incremental"to choose a GC mode (5.4+) - Utilize the
debuglibrary for traces and hooks (debug.traceback,debug.sethook) - Embed Lua in C/C++ projects via the stable C API
- Use Lua as a configuration DSL — sandbox with a custom
_ENV - Explore domain-specific dialects: LuaJIT (perf), Luau (Roblox, typed), Teal (typed)
- Cache frequently-used globals as locals (
local sin = math.sin) in hot loops - Prefer
string.formatover long..chains for complex strings - Use
select("#", ...)to handle varargs containingnilcorrectly unpackis gone — usetable.unpack(5.2+)
Continue Learning
Discover more cheatsheets to boost your productivity