Metatables? A table within a table? Maybe not
Posted on February 25, 2014 by MrNicNac
Countless times have I seen a metatable incorrectly described. There was a user on the ROBLOX Scripting Helpers forum who put it simply:
A metatable is a table inside of another table
Oh? I guess that makes them easy enough. I'll write a metatable out real quick.
local t = { m = {} }
Awesome. We have a metatable. Done. Blog post over.
Not so much. In fact, I'm not even sure where that definition arose - or how. You might be able to conceptualize it as a table within another - but that is extremely far fetched.
So, in simple terms, what is a metatable? I like to tell people it is what holds the settings for another table. How the table the metatable is linked to should act under certain conditions. It is by no means just a table inside of another.
How can you make one? You actually make two tables, and then set one as a metatable to the other. Like so:
local t1 = {} local t2 = {} setmetatable(t1, t2)
Can you guess which table is now the metatable of the other? It goes in reverse order. Read it as "t2 is now the metatable of t1."
If you haven't learned about tables, then it is discourage for you to continue reading.
So what can you do with a metatable? There are many values you can set as indexes which serve as metamethods. These are the "settings" you can attach to a table. An example of using one of the metamethods (__index).
local t1 = {} local t2 = { __newindex = function(tab, i, v) print(tab,i,v) -- tab is the table (t1) being changed -- i is the index being set -- v is the value being assigned to that index end } setmetatable(t1, t2) t1[1] = 5 print(t1[1])
It's important to note that 5 was never set to t1's first index. The metatable overrides the operation. You can choose to continue on with the setting of a new index inside the __newindex metamethod. How could you do this? Some of you might think that trying to set the index of t1 to a value inside the __newindex function might just cause an infinite loop, because the metamethod would catch itself. Over and over.
Well, you'd be right. We have to use the rawset function. This function will set an index of a table without invoking our metamethod.
local t1 = {} local t2 = { __newindex = function(tab, i, v) print(tab,i,v) -- tab is the table (t1) being changed -- i is the index being set -- v is the value being assigned to that index rawset(tab, i, v) -- set the index of tab[i] to v end } setmetatable(t1, t2) t1[1] = 5 print(t1[1])
Notice the that rawset is now in the __newindex function. If you were to run the previous code example, you would have gotten nil on the last print line. Using the second code example, you will get "5" - exactly what we set it to.
Some of you might wonder what exactly you can use metatables for. It might seem like a useless Lua feature to some of you, and maybe a gold mine to others. Creativity is a broad concept, and gives birth to even broader ideas. There are many examples of what others have done with metatables - try looking around. There might even be one or two things in ROBLOX, sitting right in front of you, that use metatables.
Commentary
Leave a Comment