Metatables
By the end of this lesson you'll use tables as both arrays and dictionaries, customise how they behave with metatables and metamethods, and build your own classes with inheritance — the foundation of every serious Lua program.
Part of the free Lua course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
💡 Real-World Analogy
Think of a table as a sheet of paper holding your data, and a metatable as a sticky note of instructions clipped to it. Normally Lua just reads the paper. But when something unusual happens — you ask for a row that isn't written down, or you try to add two sheets together — Lua reads the sticky note to find out what to do. The sticky note doesn't store data; it stores behaviour . That single idea — "look at the manual when you hit something unexpected" — is how Lua delivers default values, operator overloading, and full object-oriented programming, all from one humble data type.
1. Tables: Lua's One Data Structure
Lua has exactly one built-in data structure: the table . Remarkably, the same table is both an array (a numbered list) and a dictionary (named key → value pairs). The one rule that trips up newcomers: Lua arrays start at index 1 , not 0. You add and remove with table.insert and table.remove , and you measure length with the # operator. Read this worked example and run it before moving on.
Your turn. The snippet below is almost complete — fill in the three ___ blanks using the hints, then run it and check it against the ✅ Expected output comments.
2. Metatables & Metamethods
A metatable is an ordinary table whose special keys — called metamethods — tell Lua what to do in situations it doesn't otherwise handle. You attach one with setmetatable(t, mt) and read it back with getmetatable(t) . The most important metamethod is __index : when you read a key the table doesn't have, Lua consults __index instead of giving you nil — perfect for defaults and inheritance. Others let you intercept writes ( __newindex ) or redefine operators like + ( __add ), == ( __eq ), and printing ( __tostring ).
Key Metamethods
Metamethod
Triggered when
__index
You read a missing key
__newindex
You set a new (missing) key
__add / __sub / __mul
The + / - / * operators
__eq / __lt / __le
The == / < / <= operators
__tostring
tostring() or print()
Now you try overloading operators. Finish the Money type below so that + adds two amounts and print shows a £ sign. Fill in the two blanks:
Deep Dive: __index as a table vs a function
__index can be a table (the usual case — Lua looks the missing key up inside it) or a function that Lua calls as __index(table, key) to compute a value on demand.
Use the table form for fixed defaults and class methods; reach for the function form when the fallback must be computed each time.
3. Building OOP with Metatables
Lua has no class keyword, yet it has full object-oriented programming — you assemble it from tables and metatables. The recipe: make a table for the class, set Class.__index = Class so instances inherit its methods, and write a constructor (by convention called new ) that returns a table whose metatable is the class. Define methods with the colon ( function Animal:speak() ), which quietly adds a first parameter called self — the instance the method was called on. Inheritance is just one more metatable: point a subclass's __index at its parent.
Common Errors (and the fix)
- Off-by-one from 0-based habits: fruits[0] is nil in Lua — the first element is fruits[1] and the last is fruits[#fruits] . Loop with for i = 1, #t do .
- "attempt to index a nil value (local 'self')": you called a colon-method with a dot, e.g. dog.speak() . Use dog:speak() so self is passed automatically (it equals dog.speak(dog) ).
- "attempt to call a nil value (method 'speak')": you forgot Class.__index = Class , so instances can't find their methods. Add that line right after you create the class table.
- Mixing array and hash parts: # only counts the consecutive integer keys. In { "a", color = "red" } , #t is 1 — the color key is invisible to it. Don't rely on # for tables used as dictionaries.
- A nil in the middle of an array: setting t[2] = nil leaves a "hole", and #t may then return 1 or 3 unpredictably. Use table.remove to delete so the rest shift down.
📋 Quick Reference
Task
Code
Result
First / last item
t[1] / t[#t]
1-based
Add / remove
table.insert(t, x) / table.remove(t)
end of list
Length
#t
item count
Attach metatable
setmetatable(t, mt)
returns t
Defaults / inheritance
mt.__index = fallback
on missing key
Overload +
mt.__add = function(a,b) end
a + b
Define method
function T:method() end
self passed in
Call method
obj:method()
= obj.method(obj)
Frequently Asked Questions
Mini-Challenge: a Counter class
No blanks this time — just a brief and an outline. Build the class yourself, run it, and check your output against the comment. This is the exact shape of objects you'll write in real Lua programs and games.
🎉 Lesson Complete!
- ✅ A table is Lua's only data structure — array and dictionary, indexed from 1
- ✅ table.insert , table.remove , and # manage and measure lists
- ✅ setmetatable attaches behaviour; __index supplies defaults and inheritance
- ✅ __newindex intercepts writes; __add / __eq / __tostring overload operators
- ✅ Classes are tables with Class.__index = Class ; :method() passes self
- ✅ Next lesson: Lua for Game Development — put tables, metatables, and OOP to work in LÖVE2D and Roblox
Practice quiz
At what index does a Lua array start?
- 0
- -1
- Any number
- 1
Answer: 1. Lua tables are 1-based; the first element is t[1].
What is Lua's only built-in data structure?
- The table
- The array
- The list
- The struct
Answer: The table. The table serves as both array and dictionary in Lua.
Which function attaches a metatable to a table?
- addmeta(t, mt)
- setmetatable(t, mt)
- meta(t, mt)
- withmeta(t, mt)
Answer: setmetatable(t, mt). setmetatable(t, mt) attaches mt to t and returns t.
Which metamethod supplies a value for a missing key?
- __newindex
- __get
- __index
- __missing
Answer: __index. __index is consulted when a read finds no key on the table.
Which metamethod overloads the + operator?
- __plus
- __sum
- __concat
- __add
Answer: __add. __add defines what a + b does for tables.
What does dog:speak() pass as a hidden first argument?
- self (the dog)
- nil
- the class table
- the method name
Answer: self (the dog). The colon passes the receiver as self automatically.
Which line lets instances find their methods on the class?
- Class.methods = true
- Class.__index = Class
- setmetatable(Class)
- Class.self = Class
Answer: Class.__index = Class. Setting Class.__index = Class makes instances look up methods on the class.
Which metamethod intercepts SETTING a new key?
- __set
- __index
- __newindex
- __write
Answer: __newindex. __newindex runs when you assign to a key the table doesn't already have.
What does the # operator measure on { "a", color = "red" } ?
- 1 (only the array part)
- 2
- 0
- The number of keys
Answer: 1 (only the array part). # counts only the consecutive integer (array) part, ignoring string keys.
Which metamethod customises how a table is printed?
- __print
- __show
- __display
- __tostring
Answer: __tostring. __tostring controls the output of tostring() and print().
Continue this course
- Previous: Control Flow and Loops
- Next: Lua for Game Development