Lua Cheatsheet

Metatables and OOP

Use this Lua reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

__index for Prototypes

local Account = {}
Account.__index = Account

function Account.new(balance)
  return setmetatable({ balance = balance or 0 }, Account)
end

function Account:deposit(amount)
  self.balance = self.balance + amount
end

local a = Account.new(10)
a:deposit(5)

The table stores state. The metatable's __index points method lookup back to Account, creating prototype-style objects.

Constructor Pattern

local function new_user(name)
  return {
    name = name,
    active = true,
  }
end

Not every data shape needs metatables. Plain constructor functions are easier to inspect and serialize.

Operator Metamethods

local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
  return setmetatable({ x = x, y = y }, Vector)
end

function Vector.__add(a, b)
  return Vector.new(a.x + b.x, a.y + b.y)
end

function Vector.__tostring(v)
  return string.format('(%g,%g)', v.x, v.y)  -- %d errors on floats in 5.3+
end

Common metamethods include __index, __newindex, __call, __tostring, __eq, __lt, __add, __sub, and __len.

Inheritance

Chain __index: a miss on the instance falls back to the class, and a miss on the class falls back to its parent.

local Animal = {}
Animal.__index = Animal

function Animal.new(name)
  return setmetatable({ name = name }, Animal)
end

function Animal:speak()
  return self.name .. " makes a sound"
end

local Dog = setmetatable({}, { __index = Animal })
Dog.__index = Dog

function Dog.new(name)
  local self = Animal.new(name)
  return setmetatable(self, Dog)   -- rebrand the instance as a Dog
end

function Dog:speak()
  return self.name .. " barks"
end

local d = Dog.new("Rex")
print(d:speak())   -- "Rex barks"      (found on Dog)
print(d.name)      -- "Rex"            (found on the instance)

Lookup walks instance → DogAnimal; each level of depth adds a lookup on a miss, so keep chains short.

Read-Only Tables

local function readonly(t)
  return setmetatable({}, {
    __index = t,
    __newindex = function()
      error('attempt to modify read-only table', 2)
    end,
  })
end

The proxy itself is empty, so pairs over it yields nothing. Lua 5.2–5.3 let you add __pairs = function() return pairs(t) end to fix that, but Lua 5.4 removed the __pairs metamethod — expose the backing table or an explicit iterator function when callers need to loop.

__newindex Trap

__newindex only fires when assigning a missing key on the table. If the key already exists, Lua writes directly unless you proxy through a separate storage table.

Equality Caveat

The __eq metamethod is only used when both operands have compatible equality behavior. For domain data, explicit comparison functions are often clearer.

Module Export Pattern

local M = {}

local function private_helper() end

function M.public_api(value)
  private_helper()
  return value
end

return M

Keep private helpers local and return only the public API table. This keeps modules predictable and avoids global pollution.