Polyglot
Languages Lua stdlib
Lua § stdlib

Standard library

The Lua standard library is small but substantial — admit the principal modules: string (string manipulation, patterns), table (table operations), math (numeric functions), os (operating-system facilities), io (file I/O), package (module loading), coroutine (coroutines), debug (introspection and debugging). The standard library is intentionally minimal — admit substantial extension via libraries like Penlight (substantial general-purpose extensions), LuaSocket (networking), LuaFileSystem (file system extensions), cjson (JSON), and many others. The combination — small core library, the conventional extension via LuaRocks libraries, the embedding model where host applications expose substantial APIs — is the substance of Lua’s runtime library.

This tour points out the principal standard-library modules and their conventional uses.

string

Treated in Strings.

The principal functions:

string.len(s)                                      -- length (same as #s)
string.upper(s)
string.lower(s)
string.reverse(s)
string.rep(s, n, sep)                              -- repeat (5.3+ admits sep)
string.sub(s, i, j)                                -- substring
string.byte(s, i)                                  -- byte at i
string.char(n)                                     -- char from byte

string.format(fmt, ...)                            -- printf-style
string.find(s, pattern)
string.match(s, pattern)
string.gmatch(s, pattern)                          -- iterator
string.gsub(s, pattern, repl)                      -- substitute

For method-style:

local s = "hello"
s:upper()                                          -- equivalent to string.upper(s)
s:format(...)
s:rep(3)

table

The principal functions:

table.insert(t, [pos,] value)                      -- insert (default at end)
table.remove(t, [pos])                             -- remove (default last)
table.concat(t, [sep, [i, [j]]])                   -- join
table.sort(t, [comp])                              -- in-place sort
table.unpack(t, [i, [j]])                          -- 5.2+ (was unpack in 5.1)
table.pack(...)                                    -- 5.2+ (table from varargs)
table.move(a1, f, e, t [, a2])                     -- 5.3+ (move/copy range)

table.insert and table.remove

local arr = {10, 20, 30}

table.insert(arr, 40)                              -- {10, 20, 30, 40}
table.insert(arr, 1, 0)                            -- {0, 10, 20, 30, 40}

table.remove(arr)                                  -- {0, 10, 20, 30}; returns 40
table.remove(arr, 1)                               -- {10, 20, 30}; returns 0

table.concat

local arr = {"a", "b", "c"}

table.concat(arr)                                  -- "abc"
table.concat(arr, ", ")                            -- "a, b, c"
table.concat(arr, ", ", 2, 3)                      -- "b, c"

The function admits substantial efficiency over the .. operator in loops.

table.sort

local arr = {3, 1, 4, 1, 5, 9, 2, 6}
table.sort(arr)                                    -- {1, 1, 2, 3, 4, 5, 6, 9}

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

local people = {{name = "Bob", age = 25}, {name = "Alice", age = 30}}
table.sort(people, function(a, b) return a.age < b.age end)

table.pack and table.unpack

local args = table.pack("a", "b", "c")             -- {n=3, "a", "b", "c"}
local n = args.n                                   -- 3 (extra .n field)

local function f(a, b, c) return a, b, c end
print(f(table.unpack({"x", "y", "z"})))           -- x, y, z

-- Slice:
table.unpack({1, 2, 3, 4, 5}, 2, 4)               -- 2, 3, 4

The table.pack admits storing varargs (with explicit count); table.unpack admits unpacking back to varargs.

math

math.pi                                            -- 3.14159...
math.huge                                          -- infinity
math.maxinteger                                    -- 9223372036854775807 (5.3+)
math.mininteger                                    -- -9223372036854775808 (5.3+)

math.abs(-5)                                       -- 5
math.ceil(3.1)                                     -- 4
math.floor(3.9)                                    -- 3
math.max(1, 2, 3)                                  -- 3
math.min(1, 2, 3)                                  -- 1
math.modf(3.7)                                     -- 3.0, 0.7 (integer and fractional parts)
math.sqrt(16)                                      -- 4.0
math.exp(1)                                        -- 2.71828...
math.log(math.exp(1))                              -- 1
math.log(100, 10)                                  -- 2 (with base)
math.pow(2, 10)                                    -- 1024 (5.1; replaced by ^ in 5.2+)

math.sin(0)
math.cos(0)
math.tan(0)
math.asin(1)
math.atan(1)
math.atan(y, x)                                    -- atan2 (5.3+ as overload)

math.random()                                      -- [0, 1)
math.random(10)                                    -- [1, 10]
math.random(5, 15)                                 -- [5, 15]

math.randomseed(os.time())                         -- seed (typically with os.time())

-- 5.3+:
math.tointeger(3.0)                                -- 3
math.tointeger(3.5)                                -- nil
math.type(3)                                       -- "integer"
math.type(3.14)                                    -- "float"

os

Operating-system facilities:

os.time()                                          -- seconds since epoch
os.time({year=2026, month=1, day=15})              -- specific time
os.date()                                          -- "Wed Jan 15 10:00:00 2026"
os.date("%Y-%m-%d %H:%M:%S")                       -- formatted
os.date("*t", time)                                -- table form
os.date("!*t", time)                               -- UTC table

os.difftime(t2, t1)                                -- seconds between

os.clock()                                         -- CPU time (seconds)

os.getenv("HOME")                                  -- environment variable
os.execute("ls")                                   -- run shell command
os.exit(0)                                         -- exit with code

os.tmpname()                                       -- temp filename
os.remove(path)                                    -- delete file
os.rename(old, new)                                -- rename

Date table format:

local t = os.date("*t")
-- t = {year=2026, month=1, day=15, hour=10, min=0, sec=0, wday=4, yday=15, isdst=false}

local time = os.time(t)

io

Treated in I/O.

io.write("hello\n")
io.read()                                          -- read a line from stdin
print("hello")                                     -- alias for io.write + newline

local f = io.open("file.txt", "r")
local content = f:read("*a")                       -- entire file
f:close()

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

package

Treated in Modules and packaging.

require("modulename")
package.loaded["modulename"]                       -- cache
package.path                                       -- search path for .lua
package.cpath                                      -- search path for C modules

package.searchpath("name", path)                   -- find a module

coroutine

Treated in Coroutines.

coroutine.create(f)
coroutine.resume(co, ...)
coroutine.yield(...)
coroutine.status(co)
coroutine.wrap(f)                                  -- iterator wrapper
coroutine.running()                                -- current coroutine (or nil if main)

debug

Introspection and debugging:

debug.traceback("error", level)                    -- stack trace
debug.getinfo(level, what)                         -- info about a function
debug.getlocal(level, index)                       -- local variable
debug.setlocal(level, index, value)
debug.getupvalue(f, index)                         -- function upvalue
debug.setupvalue(f, index, value)
debug.sethook(hook, mask, count)                   -- runtime hooks
debug.gethook()
debug.getmetatable(v)                              -- access raw metatable
debug.setmetatable(v, mt)

debug.debug()                                      -- interactive debugger

The debug admits substantial introspection — conventional in development tools and frameworks; conventionally avoided in production code (admit substantial security risks).

utf8 (Lua 5.3+)

UTF-8-aware string operations:

local s = "héllo"

utf8.len(s)                                        -- 5 (characters; #s would be 6 bytes)
utf8.char(0x41)                                    -- "A"
utf8.codepoint(s, 1)                               -- code point at byte 1

for p, c in utf8.codes(s) do                       -- iterator over chars
    print(p, c, utf8.char(c))
end

utf8.charpattern                                   -- pattern matching one UTF-8 char

Common patterns

File reading

local function read_all(path)
    local f, err = io.open(path, "r")
    if not f then return nil, err end
    local content = f:read("*a")
    f:close()
    return content
end

File writing

local function write_all(path, content)
    local f, err = io.open(path, "w")
    if not f then return nil, err end
    f:write(content)
    f:close()
    return true
end

Date formatting

local now = os.date()                              -- "Mon Jan 15 10:00:00 2026"
local iso = os.date("%Y-%m-%d %H:%M:%S")           -- "2026-01-15 10:00:00"
local utc = os.date("!%Y-%m-%dT%H:%M:%SZ")         -- ISO 8601 UTC

Random selection

math.randomseed(os.time())

local arr = {"a", "b", "c", "d", "e"}
local choice = arr[math.random(#arr)]

-- Shuffle:
local function shuffle(arr)
    for i = #arr, 2, -1 do
        local j = math.random(i)
        arr[i], arr[j] = arr[j], arr[i]
    end
end

String building

local parts = {}
for i, item in ipairs(items) do
    parts[i] = tostring(item)
end
local s = table.concat(parts, ", ")

Sorting with comparator

local people = {
    {name = "Alice", age = 30},
    {name = "Bob", age = 25},
    {name = "Charlie", age = 35}
}

table.sort(people, function(a, b) return a.age < b.age end)

for _, p in ipairs(people) do
    print(p.name, p.age)
end

Computing differences

local start = os.clock()
do_substantial_work()
local elapsed = os.clock() - start
print(string.format("took %.3f seconds", elapsed))

Iterating object pairs sorted

local function sorted_pairs(t)
    local keys = {}
    for k in pairs(t) do keys[#keys + 1] = k end
    table.sort(keys)

    local i = 0
    return function()
        i = i + 1
        local k = keys[i]
        if k ~= nil then return k, t[k] end
    end
end

Number formatting

string.format("%d", 42)                            -- "42"
string.format("%05d", 42)                          -- "00042"
string.format("%.2f", math.pi)                     -- "3.14"
string.format("%e", 12345.6789)                    -- "1.234568e+04"
string.format("%x", 255)                           -- "ff"

Splitting

local function split(s, sep)
    sep = sep or "%s"
    local parts = {}
    for part in s:gmatch("[^" .. sep .. "]+") do
        parts[#parts + 1] = part
    end
    return parts
end

Joining

local s = table.concat({"a", "b", "c"}, ", ")

Random UUID-like

math.randomseed(os.time())

local function random_id(length)
    length = length or 16
    local chars = "0123456789abcdef"
    local result = {}
    for i = 1, length do
        local idx = math.random(#chars)
        result[i] = chars:sub(idx, idx)
    end
    return table.concat(result)
end

print(random_id())                                 -- e.g., "a3f2bc7e890d1234"

For true UUIDs, third-party libraries (e.g., uuid) are conventional.

Reading environment

local home = os.getenv("HOME")
local path = os.getenv("PATH")
local debug = os.getenv("DEBUG") == "1"

Running shell commands

local exit_code = os.execute("ls")                 -- runs ls; returns exit info

-- For capturing output:
local f = io.popen("ls")
if f then
    for line in f:lines() do
        print(line)
    end
    f:close()
end

Checking file existence

local function exists(path)
    local f = io.open(path, "r")
    if f then f:close(); return true end
    return false
end

Computing UTC time

local utc = os.time(os.date("!*t"))                -- UTC seconds since epoch

Third-party libraries

For substantial functionality beyond the standard library:

  • Penlight — substantial general-purpose extensions (collections, OOP, string utilities, etc.).
  • LuaSocket — TCP/UDP networking, HTTP client.
  • LuaFileSystem — directory listing, file attributes.
  • cjson / dkjson — JSON encoding/decoding.
  • LPeg — substantial parsing (PEG-style).
  • date — date/time arithmetic.
  • luaposix — POSIX functions.
  • lua-resty-* (in OpenResty) — HTTP, Redis, MySQL.
  • busted — testing framework.
  • luacheck — linting.

Install via LuaRocks:

luarocks install penlight
luarocks install dkjson

Common third-party patterns

JSON

local json = require("dkjson")

local data = {name = "Alice", age = 30}
local encoded = json.encode(data)                  -- '{"name":"Alice","age":30}'

local decoded = json.decode('{"x":1,"y":2}')
print(decoded.x, decoded.y)                        -- 1 2

HTTP via LuaSocket

local http = require("socket.http")
local body, status = http.request("https://example.com")

if status == 200 then
    print(body)
end

Directory listing via LuaFileSystem

local lfs = require("lfs")

for entry in lfs.dir(".") do
    if entry ~= "." and entry ~= ".." then
        local attrs = lfs.attributes(entry)
        print(entry, attrs.mode, attrs.size)
    end
end

A note on the conventional discipline

The contemporary Lua standard-library advice:

  • Use the standard library for the conventional core operations.
  • Use the method-call syntax (s:upper()) for substantial readability.
  • Cache string.format, table.concat etc. as locals in hot loops.
  • Use os.date, os.time for time handling.
  • Use io.lines for substantial file iteration.
  • Use math.randomseed(os.time()) before substantial random use.
  • Use LuaRocks libraries for substantial functionality (JSON, HTTP, etc.).
  • Use Penlight for substantial substrate extensions.
  • Avoid os.execute for substantial logic; use io.popen for output.
  • Use debug.traceback with xpcall for substantial error logging.

The combination — small core library covering substantial conventional needs, the LuaRocks ecosystem for substantial extensions, the host-application APIs for embedded contexts — is the substance of Lua’s runtime library. The discipline produces concise, expressive code with substantial built-in functionality plus substantial extension via the conventional library ecosystem.