โ† Back to all guides

Lua TLDR

A rapid reference guide to the Lua programming language. Lightweight, embeddable, and powerful.

v5.4.8 โ€” December 2025

What is Lua?

Lua is a lightweight, high-level scripting language designed for embedded use in applications. Known for its simplicity, speed, and powerful data description capabilities through tables.

Lightweight

Small footprint (~280KB compiled). Fast execution. Minimal dependencies. Perfect for embedding.

Simple Syntax

Clean, readable syntax. Few core concepts. Easy to learn and teach. Dynamically typed.

Powerful Tables

Tables are the only data structure. Implement arrays, dictionaries, objects, and more.

Embeddable

Designed to extend applications. C API for integration. Used in games, web servers, and more.

Basics

Hello World

-- Comments start with --
print("Hello, world!")

--[[ Multi-line
     comment ]]

Variables

-- Global by default
x = 10
name = "Alice"

-- Local variables (prefer these!)
local y = 20
local greeting = "Hello"

-- Multiple assignment
local a, b, c = 1, 2, 3
local x, y = 10  -- y is nil

-- Swap values
a, b = b, a

Control Flow

-- if statement
if x > 10 then
    print("big")
elseif x > 5 then
    print("medium")
else
    print("small")
end

-- while loop
local i = 1
while i <= 5 do
    print(i)
    i = i + 1
end

-- repeat-until (do-while)
local i = 1
repeat
    print(i)
    i = i + 1
until i > 5

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

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

-- for loop (iterator)
for k, v in pairs(table) do
    print(k, v)
end

Operators

-- Arithmetic
+ - * / % ^  -- addition, subtraction, multiplication, division, modulo, power
//           -- floor division (Lua 5.3+)

-- Relational
== ~= < > <= >=  -- equals, not equals, less, greater, etc.

-- Logical
and or not

-- String concatenation
"Hello" .. " world"  -- "Hello world"

-- Length operator
#"hello"     -- 5
#{1, 2, 3}  -- 3

Types

Lua is dynamically typed. There are 8 basic types.

Basic Types

Type Example Notes
nil nil Represents absence of value
boolean true, false Only false and nil are falsy
number 42, 3.14, 1e10 Double-precision floats (or integers in 5.3+)
string "hello", 'world' Immutable sequences of bytes
function function() end First-class values
table {1, 2, 3} The only data structure
userdata - C data (for embedding)
thread - Coroutines

Type Checking

-- type() returns a string
type(5)         -- "number"
type("hello")   -- "string"
type({})        -- "table"
type(nil)       -- "nil"
type(print)     -- "function"

-- Type coercion
tonumber("123")   -- 123
tostring(123)     -- "123"

Numbers

-- Integers (Lua 5.3+)
local x = 42

-- Floats
local y = 3.14

-- Scientific notation
local z = 1.23e-4

-- Hexadecimal
local hex = 0xFF

-- Math operations
math.floor(3.7)    -- 3
math.ceil(3.2)     -- 4
math.abs(-5)       -- 5
math.max(1, 2, 3)  -- 3
math.min(1, 2, 3)  -- 1
math.random()      -- random float [0,1)
math.random(10)   -- random int [1,10]

Tables

Tables are Lua's only data structure. They implement associative arrays and can represent arrays, sets, records, graphs, and more.

Creating Tables

-- Empty table
local t = {}

-- Array-like (1-indexed!)
local arr = {10, 20, 30}
print(arr[1])  -- 10 (not 0!)

-- Dictionary-like
local person = {
    name = "Alice",
    age = 30,
    city = "NYC"
}

-- Mixed
local mixed = {
    10, 20, 30,           -- indices 1, 2, 3
    x = 100,
    y = 200,
    ["key with spaces"] = "value"
}

Accessing Tables

-- Dot notation
person.name        -- "Alice"
person.age = 31

-- Bracket notation
person["name"]    -- "Alice"
person["age"] = 31

-- Dynamic keys
local key = "name"
person[key]       -- "Alice"

-- Length (array part only)
#arr              -- 3

Table Operations

-- Insert
table.insert(arr, 40)        -- append to end
table.insert(arr, 2, 15)     -- insert at position 2

-- Remove
table.remove(arr)            -- remove last element
table.remove(arr, 2)         -- remove at position 2

-- Concatenate
local str = table.concat(arr, ", ")  -- join with separator

-- Sort
table.sort(arr)              -- sort in place
table.sort(arr, function(a, b) return a > b end)  -- descending

Iterating Tables

-- ipairs (array part only, in order)
for i, v in ipairs(arr) do
    print(i, v)
end

-- pairs (all key-value pairs, no order guarantee)
for k, v in pairs(person) do
    print(k, v)
end

-- Manual iteration
for i = 1, #arr do
    print(arr[i])
end
Arrays in Lua are 1-indexed, not 0-indexed! This is different from most other languages.

Functions

Functions are first-class values in Lua. They can be stored in variables, passed as arguments, and returned from other functions.

Defining Functions

-- Basic function
function greet(name)
    print("Hello, " .. name)
end

-- With return value
function add(a, b)
    return a + b
end

-- Multiple return values
function minmax(a, b)
    if a < b then
        return a, b
    else
        return b, a
    end
end

local min, max = minmax(5, 3)  -- min=3, max=5

Anonymous Functions

-- Function as value
local square = function(x)
    return x * x
end

-- Pass function as argument
table.sort(arr, function(a, b)
    return a > b
end)

Variable Arguments

-- ... captures all arguments
function sum(...)
    local result = 0
    for i, v in ipairs({...}) do
        result = result + v
    end
    return result
end

sum(1, 2, 3, 4)  -- 10

-- select() for varargs
function foo(...)
    local n = select("#", ...)  -- count args
    local first = select(1, ...)  -- first arg
end

Closures

-- Function that returns function
function makeCounter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

local counter = makeCounter()
print(counter())  -- 1
print(counter())  -- 2

Tail Calls

-- Tail call optimization
function factorial(n, acc)
    acc = acc or 1
    if n <= 1 then
        return acc
    end
    return factorial(n - 1, n * acc)  -- tail call
end

Metatables & Metamethods

Metatables allow you to change the behavior of tables. They enable operator overloading, custom indexing, and more.

Basic Metatables

-- Create metatable
local mt = {
    __add = function(a, b)
        return {x = a.x + b.x, y = a.y + b.y}
    end
}

-- Attach metatable
local v1 = {x = 1, y = 2}
local v2 = {x = 3, y = 4}
setmetatable(v1, mt)
setmetatable(v2, mt)

local v3 = v1 + v2  -- {x=4, y=6}

-- Get metatable
local mt = getmetatable(v1)

Common Metamethods

Metamethod Operator/Event Description
__add + Addition
__sub - Subtraction
__mul * Multiplication
__div / Division
__eq == Equality
__lt < Less than
__le <= Less or equal
__concat .. Concatenation
__tostring tostring() String conversion
__index t[k] Reading missing keys
__newindex t[k]=v Writing to missing keys
__call t() Calling table as function

__index for Inheritance

-- Prototype-based inheritance
local Animal = {
    sound = "..."
}

function Animal:new(sound)
    local obj = {sound = sound or self.sound}
    setmetatable(obj, self)
    self.__index = self
    return obj
end

function Animal:speak()
    print(self.sound)
end

local dog = Animal:new("woof")
dog:speak()  -- "woof"

__call Metamethod

-- Make table callable
local mt = {
    __call = function(t, ...)
        print("Called with", ...)
    end
}

local t = {}
setmetatable(t, mt)
t(1, 2, 3)  -- "Called with 1 2 3"

Modules & Packages

Creating a Module

-- mymodule.lua
local M = {}

function M.greet(name)
    print("Hello, " .. name)
end

function M.add(a, b)
    return a + b
end

-- Private function
local function helper()
    return "private"
end

return M

Using Modules

-- Load module
local mymodule = require("mymodule")

-- Use module
mymodule.greet("Alice")
local sum = mymodule.add(1, 2)

-- Import specific functions
local greet = require("mymodule").greet
greet("Bob")

Module Patterns

-- Pattern 1: Table of functions (shown above)

-- Pattern 2: Single function
local function myfunction()
    return "result"
end
return myfunction

-- Pattern 3: Class-like module
local Class = {}
Class.__index = Class

function Class:new(value)
    local obj = setmetatable({}, Class)
    obj.value = value
    return obj
end

function Class:method()
    print(self.value)
end

return Class

Package Path

-- Check package search path
print(package.path)

-- Add custom path
package.path = package.path .. ";./mylibs/?.lua"

-- Loaded modules cache
print(package.loaded["mymodule"])

Coroutines

Coroutines allow you to pause and resume execution. They enable cooperative multitasking.

Creating Coroutines

-- Create coroutine
local co = coroutine.create(function()
    for i = 1, 3 do
        print("co", i)
        coroutine.yield()
    end
end)

-- Check status
print(coroutine.status(co))  -- "suspended"

-- Resume coroutine
coroutine.resume(co)  -- prints "co 1"
coroutine.resume(co)  -- prints "co 2"
coroutine.resume(co)  -- prints "co 3"
coroutine.resume(co)  -- dead, returns false

Coroutine Communication

-- Pass values with yield/resume
local co = coroutine.create(function(a, b)
    print("Started with", a, b)
    local x = coroutine.yield(a + b)
    print("Received", x)
    return x * 2
end)

local ok, sum = coroutine.resume(co, 10, 20)  -- sum = 30
local ok, result = coroutine.resume(co, 5)     -- result = 10

Coroutine Wrap

-- wrap() creates function instead of coroutine
local co = coroutine.wrap(function()
    for i = 1, 3 do
        coroutine.yield(i)
    end
end)

print(co())  -- 1
print(co())  -- 2
print(co())  -- 3

Producer-Consumer Pattern

local function producer()
    return coroutine.create(function()
        while true do
            local x = io.read()
            coroutine.yield(x)
        end
    end)
end

local function consumer(prod)
    while true do
        local ok, value = coroutine.resume(prod)
        if not ok then break end
        print(value)
    end
end

String Manipulation

String Basics

-- String literals
local s1 = "hello"
local s2 = 'world'

-- Multi-line strings
local s3 = [[
    Multi-line
    string
]]

-- Escape sequences
local s4 = "line1\nline2\ttab"

-- Concatenation
local s = "Hello" .. " " .. "world"

-- Length
#"hello"  -- 5

String Functions

-- Length
string.len("hello")      -- 5

-- Upper/lower case
string.upper("hello")    -- "HELLO"
string.lower("HELLO")    -- "hello"

-- Substring
string.sub("hello", 2, 4) -- "ell" (1-indexed!)
string.sub("hello", -3)   -- "llo" (negative = from end)

-- Find
string.find("hello world", "world")  -- 7, 11 (start, end)

-- Replace (first occurrence)
string.gsub("hello world", "world", "Lua")  -- "hello Lua"

-- Replace all
string.gsub("aaa", "a", "b")  -- "bbb"

-- Reverse
string.reverse("hello")  -- "olleh"

-- Repeat
string.rep("ab", 3)      -- "ababab"

Pattern Matching

-- Pattern classes
-- %a = letters, %d = digits, %s = space
-- %w = alphanumeric, %p = punctuation
-- . = any character, %x = hex digit

-- Match
string.match("hello123", "%d+")     -- "123"
string.match("foo@bar.com", "@(.+)")  -- "bar.com"

-- Global match (iterator)
for word in string.gmatch("hello world lua", "%w+") do
    print(word)
end

-- Substitution with pattern
string.gsub("hello 123", "%d+", "456")  -- "hello 456"

String Formatting

-- C-style formatting
string.format("%.2f", 3.14159)              -- "3.14"
string.format("%d", 42)                     -- "42"
string.format("%s %s", "Hello", "world")  -- "Hello world"
string.format("%x", 255)                   -- "ff" (hex)

Error Handling

Raising Errors

-- error() raises an error
function divide(a, b)
    if b == 0 then
        error("division by zero")
    end
    return a / b
end

-- assert() checks condition
function divide(a, b)
    assert(b ~= 0, "division by zero")
    return a / b
end

Catching Errors

-- pcall (protected call)
local ok, result = pcall(divide, 10, 0)
if ok then
    print("Result:", result)
else
    print("Error:", result)  -- result is error message
end

-- xpcall (with error handler)
local function errorHandler(err)
    return "ERROR: " .. err
end

local ok, result = xpcall(divide, errorHandler, 10, 0)

File I/O

-- Read entire file
local file = io.open("file.txt", "r")
if file then
    local content = file:read("*all")
    file:close()
end

-- Write to file
local file = io.open("file.txt", "w")
if file then
    file:write("Hello, world!\n")
    file:close()
end

-- Read line by line
for line in io.lines("file.txt") do
    print(line)
end

-- Modes: "r" (read), "w" (write), "a" (append)
-- "r+" (read/write), "w+" (read/write, truncate)
Always use pcall for operations that might fail (file I/O, network calls, etc).