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
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)
pcall for operations that might fail (file I/O, network calls, etc).