User Tools

Site Tools


developer_center:developer_editor:script:introduction_to_metatable_in_lua

What Are Metatables?

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

List of Rule

Here are some common behaviors you can modify:

  • Accessing a Non-Existent Key (__index)
  • Setting a New Key-Value Pair (__newindex)
  • Arithmetic Operations (__add, __sub, __mul, __div, __mod, __pow, __unm)
  • Concatenation (__concat)
  • Length Operator (__len)
  • Equality Comparison (__eq)
  • Less Than and Less Than or Equal Comparisons (__lt, __le)
  • Function Call (__call)
  • Table String Representation (__tostring)
  • Table Garbage Collection (__gc)

Usage & Example

Metatable are often used for framework or creating code that is reuseable.

1. Accessing a Non-Existent Key (__index)

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..


2. Setting a New Key-Value Pair (__newindex)

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.


3. Arithmetic Operations (__add, __sub, __mul, __div, __mod, __pow, __unm)

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.


4. Concatenation (__concat)

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.

5. Length Operator (__len)

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

6. Equality Comparison (__eq)

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.

7. Less Than and Less Than or Equal Comparisons (__lt, __le)

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.

8. Function Call (__call)

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.

9. Table String Representation (__tostring)

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

10. Table Garbage Collection (__gc)

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
developer_center/developer_editor/script/introduction_to_metatable_in_lua.txt · Last modified: 2024/07/15 23:56 by hhuxx