Metatables in a nutshell

Metatables, metatables are tables that make regular tables more stronger. By giving regular tables more functionality rather than just be used as a list. Think of it as a someone giving admin commands to a player.


Metatables

As said before, metatables are tables that make tables more stronger.

We all know what tables are, they are data that hold keys and values. Metatables allow tables to have more than just keys and values.

Let me show you how to set a metatable to a table.

local t1 = {}
local t2 = {}

setmetatable(t1, t2) -- t1's metatable is set to t2

-- This is also okay too
local newtable = setmetatable({}, t2)

And in case if you want to get the metatable, just do this

local t1 = {}
local t2 = {}

setmetatable(t1, t2)

getmetatable(t1) -- returns t2 (the metatable of t1)

Metamethods

Now that we have metatables, we can now use metamethods, think of the metatables as an admin system and the metamethods are admin commands.

1. __newindex and __index

So, how do we use metamethods? Metamethods are functions that are stored in the metatable that give extra abilities to the regular table.

Two metamethods that are widely-used and are really useful is __index and __newindex.

Let’s go over the __index one, __index is a metamethod that gets fired when something that is nil (so, table[index] = nil) is indexed.

local t = {
	x = 1,
	y = 2,
	z = 3
}
local mt = {
	--[[
		__index is the metamethod,
		table is the "weak" table that was called for this metamethod,
		key is the key that was called
	]]
	__index = function(table, key)
		print(key .. " is not a value in this table")
		-- you can also return a value as a placeholder! (ex: return 1)
	end
}

setmetatable(t,mt) -- as always set the metatable

print(t.x) -- this will print its value, and __index wouldn't be fired
print(t.b) -- this will fire __index and print nil
Fun fact about __index

Instead of using a function, you can also use a table too!

What this does, is basically if you table[index] something that is included in the __index table, it will return that table[index] value from the __index table.

local t = {
	x = 1,
	y = 2,
	z = 3
}
local mt = {
	__index = {b = 420}
}

setmetatable(t,mt) -- as always set the metatable

print(t.x) -- as usual, this will print 1
print(t.b) -- this will print 420, because in the __index table, a is equal to 420, although not being even in the weak table
print(t.a) -- this won't fire __index (since it is a table, not a function) but instead print nil

Moving onto, __newindex. Like, __index this gets fired when we attempt to set a value to an index (table[index] = value) and if a function is set, it will ignore setting the value, but instead fire the function.

REMEMBER! This only works for indexes that are nil, so if table[index] already exists and your attempting to change the value for it, it won’t fire __newindex!

Here is a code example:

local t = {
	x = 1,
	y = 2,
	z = 3
}
local mt = {
	--[[
		__newindex is the metamethod,
		table is the "weak" table that was called for this metamethod,
		index is the index that was trying to be set,
		value is the value that was trying to be set to the index
	]]
	__index = function(table, index, value)
		print("attempted to set", index, "to", value)
	end
}

setmetatable(t,mt) -- as always set the metatable

t["x"] = 0 -- will not fire __newindex, cause t["x"] is already indexed!
t["xyz"] = 0 -- will fire __newindex, since t["xyz"] is not indexed, and since the __newindex function intercepts with setting the index, this will not be added to the table!

2. Metamethods That Add Compatibility

You know how I said metamethods add extra abilities to tables, not only it can add more abilities to indexing tables, it can also allow tables to be used in math operations, be called as a function (table()), be called as a string ("foo" .. {}), etc.

I’ll give one example of a metamethod that allows tables to be used in adding.

local t = {}
local mt = {
	--[[
		__add is a metamethod,
		table is the regular table,
		addingValue is the value that was trying to be added to the table
	]]
	__add = function(table, addingValue)
		print("adding", addingValue, "to 4!")

		return 4 -- the returned value will be the value being added to addingValue
	end
}

setmetatable(t, mt) -- As always set the metatable

print(t + 5) -- prints: "9"
print({} + 1) -- for reference, this will throw an error: "1: attempt to perform arithmetic (add) on table and number"

And, that’s metatables in a nutshell. There so many ways you can use metatables to your advantage and many other metamethods you can use. This is just a simple explanation of metatables for people who are very new and don’t like reading as much.

If you are still confused, you can ask any questions!

4 Likes

IMO, this is the best metatable nutshell post I’ve read. The post is short, words are clear, and basically doesn’t have extra use of unnecessary words.

The post is lacking the explanation of some must needed metamethods like __index and __newindex, but other than that, good job!

3 Likes

I wanted to add those, but I tried to keep it short and simple. But, maybe I might add it.

You can just add the explanation about the other metamethods under details

view explanation

Yeah, like this

1 Like

done!