An Introduction to Metatables

Many people seem to be getting confused by metatables and metamethods, so I’m going to write a guide on them.

So what exactly is the difference between a metatable and a regular table? Well, there isn’t one. Any table can act as a metatable for itself or for other tables. The actual usage of a metatable by the language comes into play when you use setmetatable and getmetatable. If a table has a metatable defined, then you can alter how it works (mostly via operator overloading) using metamethods.

Some metamethods are only called under certain circumstances, an example would be the _index metamethod when the thing indexed was nil. How this works is when the language tries to index a table using a key and there’s nothing there, it then looks for an __index metamethod, and if it finds one, it hands over control to it.

local MT = {}
function MT.__index(self, key)
    return 0
end

local t = setmetatable({}, MT)

print(t.something) --> 0

So right here, the value can’t be found and so the return value of __index is used instead, and so it returns 0 rather than nil as it normally would. I’ll give another example:

local MT = {}
function MT.__call(self, ...)
    return self[math.random(1, #self)]
end

local t = {"Hello", "Good", "Sir", "How", "Are", "You"}
setmetatable(t, MT)
for i = 1, 15 do
    print(t())
end
--> You Sir Are Are You Good Sir Are Good How Sir How Sir How You

The __call metamethod lets us put a method in place for when someone attempts to call the table. Normally this would cause an error because tables are tables and not functions, but in this case, it’s possible thanks to metatables.

If you wish to grab a value without setting off any metamethods in a table, use rawget(t, key), likewise use rawset(t, key, value) for setting without provoking a metatable.

There’s also about more 17 metamethods, but it’d take me ages to go over all of them, so I’ll link you to them here.

12 Likes

This topic was automatically closed after 1 minute. New replies are no longer allowed.