A metatable in Lua defines various extraneous behaviors for a table when indexed, modified, interacted with, etc. They are Lua's core metaprogramming feature; most well known for being useful to emulate classes much like an OOP language.
Any table (and userdata) may be assigned a metatable. You can define a metatable for a table as such:
-- Our sample table
local tab = {}
-- Our metatable
local metatable = {
-- This table is then what holds the metamethods or metafields
-- that Lua will read to determine the table's behavior, eg.
__index = function() --[[ ... ]] end
}
setmetatable(tab, metatable)The metatable can be retrieved back (unless the __metatable metafield is set):
local metatable = getmetatable(tab) -- Returns nil if tab has no metatable setAfterwards, you may remove a metatable and restore regular functionality as such:
setmetatable(tab, nil)Here are the metamethods that you can define, and their behavior:
In an argument signature like f(a, b), a and b don't necessarily have to be instances of your metatable. One of them will always be, but not necessarily the first. Beware!
If any operand for an operation is not a number, Lua will try to call a metamethod. It starts by checking the first operand (even if it is a number); if that operand does not define a metamethod for the operation, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, if no metamethod is found, Lua raises an error.
__add(a, b): the addition (+) operation.__sub(a, b): the subtraction (-) operation.__mul(a, b): the multiplication (*) operation.__div(a, b): the division (/) operation.__unm(a): the negation (unary -) operation.__mod(a, b)(Lua 5.1): the modulo (%) operation.__pow(a, b)(Lua 5.1): the exponentiation (^) operation.__idiv(a, b)(Lua 5.3): the floor division (//) operation.
Behavior similar to calculation operators, except that Lua will try a metamethod if any operand is neither an integer nor a float coercible to an integer (see §3.4.3).
__band(a, b): the bitwise AND (&) operation.__bor(a, b): the bitwise OR (|) operation.__bxor(a, b): the bitwise exclusive OR (binary ~) operation.__bnot(a): the bitwise NOT (unary ~) operation.__shl(a, b): the bitwise left shift (<<) operation.__shr(a): the bitwise right shift (>>) operation.
__eq(a, b): the equal (==) operation. Behavior similar to calculation operators, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean.__lt(a, b): the less than (<) operation. Behavior similar to calculation operators, except that Lua will try a metamethod only when the values being compared are neither both numbers nor both strings. Moreover, the result of the call is always converted to a boolean.__le(a, b): the less equal (<=) operation. Behavior similar to the less than operation.
__concat(a, b): the concatenation (..) operation. Behavior similar to calculation operators, except that Lua will try a metamethod if any operand is neither a string nor a number (which is always coercible to a string).__len(a)(Lua 5.1): the length (#) operation. If the object is not a string, Lua will try its metamethod. If there is a metamethod, Lua calls it with the object as argument, and the result of the call (always adjusted to one value) is the result of the operation. If there is no metamethod but the object is a table, then Lua uses the table length operation (see §3.4.7). Otherwise, Lua raises an error.
__index: The indexing access operationtable[key]. This event happens whentableis not a table or whenkeyis not present intable. The metavalue is looked up in the metatable oftable. The metavalue for this event can be either a function, a table, or any value with an__indexmetavalue. If it is a function, it is called withtableandkeyas arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, the final result is the result of indexing this metavalue withkey. This indexing is regular, not raw, and therefore can trigger another__indexmetavalue. Examples:local data = {foo = 'bar'} local proxy = setmetatable({}, {__index = data}) print(proxy.foo) --> 'bar'
local tab = setmetatable({count = 0}, { __index = function(self, _k) self.count = self.count + 1 return self.count end }) print(tab.indextest) --> 1 print(tab.indextestagain) --> 2 print(tab.asdfasdf) --> 3 print(tab[1234]) --> 4
__newindex: The indexing assignmenttable[key] = value. Like the index event, this event happens whentableis not a table or whenkeyis not present intable. The metavalue is looked up in the metatable oftable. Like with indexing, the metavalue for this event can be either a function, a table, or any value with an__newindexmetavalue. If it is a function, it is called withtable,key, andvalueas arguments. Otherwise, Lua repeats the indexing assignment over this metavalue with the same key and value. This assignment is regular, not raw, and therefore can trigger another__newindexmetavalue. Whenever a__newindexmetavalue is invoked, Lua does not perform the primitive assignment. If needed, the metamethod itself can call rawset to do the assignment. Examples:local t = setmetatable({}, { __newindex = function(t, key, value) if type(value) == 'number' then rawset(t, key, value * value) else rawset(t, key, value) end end }) t.foo = 'foo' t.bar = 4 t.la = 10 print(t.foo) --> 'foo' print(t.bar) --> 16 print(t.la) --> 100
__call(args): The call operationfunc(args). This event happens when Lua tries to call a non-function value (that is,funcis not a function). The metamethod is looked up infunc. If present, the metamethod is called withfuncas its first argument, followed by the arguments of the original call (args). All results of the call are the results of the operation. This is the only metamethod that allows multiple results.
__mode: Controls how "weak" a table is. If present, must be one of the following strings:"k", for a table with weak keys;"v", for a table with weak values; or"kv", for a table with both weak keys and values. A table with weak keys and strong values is also called an ephemeron table. In an ephemeron table, a value is considered reachable only if its key is reachable. In particular, if the only reference to a key comes through its value, the pair is removed. (see §2.5.4)__close(value, err?)(Lua 5.4): Called when a variable is closed (see §3.3.8).__gc(): Called when the the garbage collector detects that the corresponding table or userdata is dead. See §2.5.3
__tostring(): If the metatable ofvhas a__tostringfield, thentostringcalls the corresponding value withvas the argument, and uses the result of the call as its result. Otherwise, if the metatable ofvhas a__namefield with a string value, tostring may use that string in its final result.__metatable: Changes the behavior ofgetmetatable. If object does not have a metatable, returnsnil. Otherwise, if the object's metatable has a__metatablefield, returns the associated value. Otherwise, returns the metatable of the given object.__name: Used very rarely internally (seeluaL_newmetatable) and bytostring()if the__tostringmetamethod is missing.__pairs()(Lua 5.2): Affects iteration when using thepairs()function, letting you define a custom iterator function (seepairs(),next()).__ipairs()(Lua 5.2 - Lua 5.3): Same as__pairs(), except for theipairs()function. Deprecated as of Lua 5.4
__iterator()(MoonSharp): Called if the argumentfof afor ... in ...loop is not actually a function.__usedindex()(SRB2): Called when assigning an index/value to an existing key/variable.
https://www.lua.org/manual/5.4/manual.html#2.4
https://web.archive.org/web/20230330043254/https://ebens.me/post/lua-metatables-tutorial/
https://www.lua.org/versions.html
https://www.lua.org/manual/5.4/contents.html#index