Meta tables in Lua are special tables that let you change how regular tables behave. Think of them as “rules” you can set to control what happens when you do certain actions with a table. The logic is very simple and not confusing at all.
Here is you have normal Table
regular_Table = {} meta_rule = {} -- your rule here
Here is the Function to set Metatable
setmetatable(regular_Table, meta_rule)
Now Table regular_table is set it meta methods using rule in meta_rule. But in the example we did nothing to Table regular_table
Here are some common behaviors you can modify:
__index
)__newindex
)__add
, __sub
, __mul
, __div
, __mod
, __pow
, __unm
)__concat
)__len
)__eq
)__lt
, __le
)__call
)__tostring
)__gc
)Metatable are often used for framework or creating code that is reuseable.
You can define what happens when you try to access a key that does not exist in a table.
local myTable = {} local meta = { __index = function(table, key) return "This key does not exist!" end } setmetatable(myTable, meta) print(myTable.nonExistentKey) -- Output: This key does not exist!
From the example above, you can modify the code so that when a not exist index is accessed, it executes a function that outputs This key does not exist!
.
You can also adjust the function to perform specific actions based on your needs, such as adding the key with a default value..
You can control the behavior when adding a new key-value pair to a table.
local myTable = {} local meta = { __newindex = function(table, key, value) print("New key-value pair added:", key, value) end } setmetatable(myTable, meta) myTable.newKey = "newValue" -- Output: New key-value pair added: newKey newValue
Notice that the rule contained in Table meta
is __newindex
, which sets the rule for adding a new index. This rule will execute __newindex(table, key, value)
. The parameters are constant.
You can define custom behaviors for arithmetic operations involving tables.
local table1 = {1, 2} local table2 = {3, 4} local meta = { __add = function(a, b) return {a[1] + b[1], a[2] + b[2]} end } setmetatable(table1, meta) local result = table1 + table2 print(result[1], result[2]) -- Output: 4 6
In Lua, the default behavior when you attempt to perform arithmetic operations (like addition, subtraction, multiplication, etc.) on tables is to raise an error. Lua does not have built-in support for arithmetic operations on tables, as tables are not designed to be used directly as numeric or mathematical entities. They are primarily used as associative arrays (dictionaries) and lists.
With metatables you could possibly set the behavior,methods when doing arithmetic operations using tables. But it will only work to metatables you defined.
You can define custom behavior for the concatenation of tables.
local table1 = {1, 2} local table2 = {3, 4} local meta = { __concat = function(a, b) return table.concat(a, ",") .. "," .. table.concat(b, ",") end } setmetatable(table1, meta) local result = table1 .. table2 print(result) -- Output: 1,2,3,4
By default, Lua does not support concatenation of tables using the concatenation operator (..). Attempting to concatenate tables without defining a custom behavior will result in an error.
You can define custom behavior for calculating the length of a table.
local myTable = {1, 2, 3} local meta = { __len = function(t) return 42 -- Just an arbitrary value end } setmetatable(myTable, meta) print(#myTable) -- Output: 42
The default behavior of the length operator (#) in Lua when applied to tables is to return the length of the array part of the table. Lua tables can be used both as arrays (sequential lists) and as dictionaries (key-value pairs). The # operator counts the number of elements in the array part, which is defined as the longest sequence of non-nil values starting from index 1.
It doesn't count dynamic changes when you use table.insert
and then using table.remove
in middle of the index
But you could modify it like this.
-- Example table local myTable = {1, 2, 3, nil, 5, key1 = "value1", key2 = "value2"} print(#myTable) -- Output: 3 -- Metatable with __len metamethod local meta = { __len = function(t) local count = 0 for k, v in pairs(t) do if v ~= nil then count = count + 1 end end return count end } -- Set metatable for myTable setmetatable(myTable, meta) -- Now, using the length operator (#) on myTable will invoke the __len metamethod print(#myTable) -- Output: 7
Or you can return 0 for security reasons
You can define custom behavior for comparing two tables for equality.
local table1 = {1, 2} local table2 = {1, 2} local meta = { __eq = function(a, b) return a[1] == b[1] and a[2] == b[2] end } setmetatable(table1, meta) setmetatable(table2, meta) print(table1 == table2) -- Output: true
You can override table comparison behavior by setting a metatable with __eq
(for == and ~= ) and other metamethods to define custom comparison rules based on your application's requirements.
You can define custom behavior for comparing two tables using < and ⇐.
local table1 = {1} local table2 = {2} local meta = { __lt = function(a, b) return a[1] < b[1] end } setmetatable(table1, meta) setmetatable(table2, meta) print(table1 < table2)
Lua does not provide built-in mechanisms to compare tables based on their contents directly using relational operators. You must iterate through and compare elements manually if content-based comparison is needed.
You can make table behave like function
local myTable = {} local meta = { __call = function(table, arg) return "Called with argument: " .. arg end } setmetatable(myTable, meta) print(myTable("Hello")) -- Output: Called with argument: Hello
When you set __call
, the first parameter will always be the table itself. You can add more parameters after that.
The same applies when you use ( : )
to call a function inside a table.
You can define how a table is converted to a string.
local myTable = {1, 2, 3} local meta = { __tostring = function(t) return "This is my custom table!" end } setmetatable(myTable, meta) print(myTable) -- Output: This is my custom table!
Notice it print(myTable)
without running the function myTable()
.
By default when you Print() function it will return something like functionx123ZEW
You can define custom behavior when a table is collected by the garbage collector.
local myTable = {} local meta = { __gc = function(t) print("Table is being garbage collected") end } setmetatable(myTable, meta) myTable = nil collectgarbage() -- Output: Table is being garbage collected