local metatable = {}
metatable["Hey"] = 23
metatable.__index = metatable
local CreatedTable = setmetatable({},metatable)
print(CreatedTable["Hey"])
I don’t understand the above code, is “Hey” a key in the metatable which 5 was assigned to? What does setting the metatable.__index to itself do anyway? Does it add a missing index? A step by step process of what the script is doing would be greatly appreciated.
Every tutorial and documentation I’ve watched or read on setting the metatable to itself has either explained it poorly or is too convoluted to understand.
It’s pretty simple actually. You have a number attached to a key named “Hey” in your metatable and you attach this metatable to an empty table with setmetatable() function (The function itself returns it’s first argument when called so CreatedTable variables references to that empty table.).
Then when you try to get the value attached to “Hey” key in CreatedTable table it will fire the __index metamethod in that metatable (Since no value is attached to “Hit” in CreateTable so lua automaticly searched for metatable and __index metamethod in it.) and since the value attached to __index metamethod is the metatable itself the search essentially turns into metatable["Hey"] so lua will get the value attached to “Hit” key in the metatable which is 23.
Of course trying to index the table with y will give you nil since it was never set. What about this?
local class = {x = 1}
local mt = {__index = {y = 2}}
setmetatable(class, mt)
print(class.x, class.y) --> 1, 2
It works, but it’s the same as doing this:
local class = {x = 1}
local mt = {}
mt.y = 2
mt.__index = mt
setmetatable(class, mt)
What the _index metamethod does is that whenever the value in the table at a specific key is nil, lua will refer to the table that was assigned to the metamethod and find an index there, if a table is assigned, because you can also pass a function. In the second and third code above, they refer to the same table, since in the third code, it refers to the metatable itself, which is a table with the index y = 2.
This is vital in OOP, as you’d need to refer to the methods of your class. Without the _index metamethod, you’d have to define all of the methods in a new table in the constructor function, which is messy:
local car = {}
car.__index = car -- without this, you wouldn't be able to refer to its methods
function car.new(name)
return setmetatable({name = name}, car)
end
function car:GetName()
return self.Name
end
local c = car.new("hi")
print( c:GetName() ) --> hi
.__index is a metamethod that is fired when you look for a key in a table that is associated with a nil value.
local Table = {}
setmetatable(Table, {
__index = function(self, Index)
return "undefined"
end
})
print(Table.hello) --// outputs undefined instead of nil.
--// There is no `hello` key defined in `Table` so `__index` is invoked with `Table` and `"hello"`
A good quirk to know about .__index is that the self parameter represents the table that had its metamethod invoked.
People usually do meta.__index = meta because they’re working with OOP. It is convenient to put all of your methods and metamethods together when constructing a “class” in Lua.
local Class = {}
Class.__index = Class
function Class.new()
local self = setmetatable({}, Class)
return self
end
--// Because each new Class object has `Class` as its metatable, when you do
--// NewObject:Hello() or NewObject + ... it will invoke the __index method of Class which loops back to Class
--// which stores the methods "Hello" and the overload __add
function Class:Hello()
print"hello world"
end
function Class:__add()
return 0
end
local NewObject = Class.new()
NewObject:Hello()
print(NewObject + 5)
Yes, if there is no value attached to that key in metatable and there is no metatable with a __index metamethod attached to the metatable then it will return nil.
So the metatable refers to itself which is 2, that’s why it prints 2? You can create any key in the metatable and assign a value to it on the spot? Like
The metatable itself isn’t 2, it’s a table that contains 2 with an index of “y”.
Yes, you can do that:
local tbl = {x = 1}
local mt = {}
mt.__index = mt
mt.y = 2
mt.z = 3
mt.a = 4
mt.b = 5
setmetatable(tbl, mt)
print(tbl.x) --> 1; the index "x" exists in the table
print(tbl.y, tbl.z, tbl.a, tbl.b) --> 2, 3, 4, 5; the indices "y," "z", "a" and "b" don't exist in the table, so it refers to the table assigned to its attached metatable's __index metamethod, essentially taking its value
If you search a table for a value that doesn’t exist, it’ll check if the table has a metatable. If so, it’ll check if the value that __index has, has the value you tried to get, if it does, it’ll return it.
“Index” is like
local myTable = {}
local mt = {}
mt.one = 1
mt.__index = mt -- if something is indexed, it'll check the value that __index is hooked too
setmetatable(myTable, mt)
print(myTable.one) -- prints "1"
So the __index is hooked to the metatable itself which is the “value” you’re referring to right? and it contains 1, so the script prints out 1. Am I understanding the script process corectly?
It seems people are explaining what __index does, but the original question was why was it set to its own table?
Metatables are just normal tables (with metamethods or, as has been discussed, keys looked-for in a
specified table, which allows chaining and classes.) The point is that they are just tables, so there’s
nothing stopping you from, for instance, setting a table’s metatable to itself–Something that’s commonly
done with Roblox because of the lack of separate automatic metatable marshaling–So since they are just
tables, why create two tables in cases where cramming everything you need into one table would suffice?
(eg. for a base class.) Edit: Fix New Year’s tipsy typo.