Reading — step 1 of 5
Learn
Metatables and __index
Every table you have written so far answers a missing key the same way:
local t = {size = 3}
print(t.colour)
--> nil
A metatable lets you change that answer. It is an ordinary table whose keys
have special names, attached with setmetatable, and it is the mechanism behind
inheritance, operator overloading and every Lua "class" you will ever read.
__index as a fallback table
local defaults = {colour = "black", size = 1}
local item = setmetatable({size = 3}, {__index = defaults})
print(item.size)
print(item.colour)
print(rawget(item, "colour"))
--> 3
--> black
--> nil
Read the third line carefully, because it is the whole idea. item.colour is
"black", but item does not have a colour field — rawget looks in the
table without consulting the metatable and finds nothing. The lookup only falls
through to defaults when the key is genuinely absent, so item.size still
answers 3.
__index as a function
If the fallback is a computation rather than a lookup, give __index a function.
It receives the table and the missing key:
local squares = setmetatable({}, {__index = function(_, k) return k * k end})
print(squares[7], squares[9])
--> 49 81
Operators
The same trick names the arithmetic and comparison hooks. __tostring is the one
you will reach for first, because it fixes print:
local P = {}
P.__index = P
P.__tostring = function(p) return "(" .. p.x .. "," .. p.y .. ")" end
P.__add = function(a, b) return setmetatable({x = a.x + b.x, y = a.y + b.y}, P) end
local function newP(x, y) return setmetatable({x = x, y = y}, P) end
print(tostring(newP(1, 2)))
print(tostring(newP(1, 2) + newP(10, 20)))
--> (1,2)
--> (11,22)
__eq, __lt, __len and __call work the same way — __len is what #
consults, and __call makes a table usable as a function.
Notice P.__index = P. That line is the class pattern: the metatable points at
itself, so any method stored on P is found by every table created with it as a
metatable. The next lesson builds on exactly that.
Common mistakes
- Expecting
__indexto fire for a key that exists — it is consulted only when the key is absent, which is why an own field always wins. - Confusing
rawgetwith normal indexing —t.kmay come from the metatable;rawget(t, "k")never does. Use it when you need to know which. - Forgetting
setmetatablereturns the table — that is whyreturn setmetatable({...}, P)is the idiom rather than three lines. - Setting
__indexto a function that forgets to return — the caller then seesniland the fallback looks broken. - Assuming metatables are inherited automatically — each table needs its own
setmetatablecall; the constructor is what makes that reliable.
Your exercise
Config With Defaults builds the fallback pattern and then reports which side answered.
defaults holds colour = "black" and size = "1", and config starts empty
with defaults behind it. Read a count, then that many commands:
SET key valuestores intoconfigitselfGET keyprints one line
For a GET, print key=value (own) when the key is in config directly,
key=value (default) when the value came from defaults, and key=none when
neither has it. Telling the first two apart is what rawget is for — plain
indexing cannot, because it returns the same value either way.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…