100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Metamethods and Operator Overloading

Learn how Lua's arithmetic, comparison, and conversion metamethods let custom tables respond naturally to +, ==, tostring(), and even direct function-call syntax.

Tables & DataIntermediate9 min readJul 10, 2026
Analogies

Overloading Operators with Metamethods

Beyond __index and __newindex, Lua defines a family of metamethods that let ordinary tables respond to operators the way built-in numbers and strings do. Writing my_vector + other_vector, my_matrix == other_matrix, or tostring(my_object) all work on plain tables once the appropriate metamethod -- __add, __eq, __tostring, and so on -- is defined in the table's metatable. This is Lua's version of operator overloading: there is no special class syntax for it, just metamethods that the interpreter checks automatically whenever the corresponding operator or standard function is used on a table.

🏏

Cricket analogy: The Duckworth-Lewis-Stern method redefines how 'target score' is calculated when rain interrupts a match, overriding the sport's default arithmetic just as __add overrides the default meaning of the + operator for a custom Lua type.

Arithmetic Metamethods

Arithmetic metamethods include __add, __sub, __mul, __div, __mod, __pow, and __unm (for unary negation), each corresponding directly to +, -, *, /, %, ^, and unary -. When Lua evaluates a + b, it first checks whether a is a number; if not (or if b isn't), it looks for __add in either operand's metatable and calls it as function(a, b), so a common pattern is defining a Vector class where v1 + v2 returns a brand-new vector whose components are the sums of v1's and v2's, without mutating either input.

🏏

Cricket analogy: Combining two batsmen's partnership contributions into a total isn't just their runs added in isolation -- strike rotation and boundary counts matter too, similar to how __add lets a Vector's + operator compute a genuinely new combined result rather than a naive sum.

Comparison and Concatenation Metamethods

Comparison metamethods __eq, __lt, and __le back the ==, <, and <= operators respectively (> and >= are implemented by Lua automatically flipping the operands into __lt/__le calls), but note that __eq is only even consulted when both operands are tables (or both userdata) of the same primitive type -- Lua never calls __eq to compare a table against a number or string. Separately, __concat handles the .. operator when at least one operand isn't a string or number, which lets you define how a custom object should be stringified and joined, for instance letting 'Score: ' .. scoreObject work naturally instead of raising a type error.

🏏

Cricket analogy: Deciding whether two bowling figures are 'equal' for a record book requires comparing overs, maidens, runs, and wickets together as one unit, not just one number, similar to how __eq lets two Lua tables define custom multi-field equality instead of Lua's default identity check.

__tostring and __call

The __tostring metamethod is called automatically by the built-in tostring() function (and therefore by print(), since print calls tostring on each argument), letting an object return a human-readable string like 'Vector(3, 4)' instead of the default 'table: 0x...' address. The __call metamethod goes further: if it's defined, you can invoke a table directly as if it were a function, obj(args), and Lua calls __call(obj, args) behind the scenes -- a technique used to build callable objects such as memoized function wrappers or configurable event handlers.

🏏

Cricket analogy: A broadcast graphic shows 'V Kohli: 82(61)' instead of a raw database row, just as __tostring turns a table into a readable string for print(); a bowling machine you 'call' with a speed setting to fire a delivery is like __call letting an object be invoked directly.

lua
local Vector = {}
Vector.__index = Vector

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

-- Overload + for Vector objects
function Vector.__add(a, b)
  return Vector.new(a.x + b.x, a.y + b.y)
end

-- Overload == for Vector objects
function Vector.__eq(a, b)
  return a.x == b.x and a.y == b.y
end

-- Readable printing
function Vector.__tostring(v)
  return string.format("Vector(%g, %g)", v.x, v.y)
end

local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2
print(v3)                       --> Vector(4, 6)
print(v1 == Vector.new(1, 2))   --> true

-- __call: make an object directly invokable
local Counter = setmetatable({ count = 0 }, {
  __call = function(self, step)
    self.count = self.count + (step or 1)
    return self.count
  end
})
print(Counter())    --> 1
print(Counter(5))   --> 6

__eq is only consulted when both operands are tables (or both are userdata) -- Lua never calls __eq to compare a table with a number, string, boolean, or nil; such comparisons are always false without invoking any metamethod. Also, in Lua 5.3 and later, __eq is only triggered when the two tables are not already primitively equal (i.e., not the exact same table reference), since identical references are always == regardless of metamethods.

  • Arithmetic metamethods (__add, __sub, __mul, __div, __mod, __pow, __unm) let custom tables respond to +, -, *, /, %, ^, and unary minus.
  • Comparison metamethods __eq, __lt, and __le back ==, <, and <=; Lua derives > and >= by flipping operands.
  • __eq only fires when both operands are tables of the same primitive type; comparing a table to a non-table never invokes it.
  • __concat handles .. when at least one operand isn't already a string or number, enabling readable concatenation of custom objects.
  • __tostring is called by tostring() and therefore by print(), letting objects control their own readable representation.
  • __call lets a table be invoked like a function, obj(args), which is used for callable wrappers, memoization, and configurable handlers.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#LuaStudyNotes#MetamethodsAndOperatorOverloading#Metamethods#Operator#Overloading#Operators#StudyNotes#SkillVeris#ExamPrep