luaguides

setmetatable

setmetatable(table, metatable)

Assigns a metatable to a table. The metamethods inside the metatable control how the table behaves when you use operators, index it, or call it like a function.

Signature

setmetatable(table, metatable)

table is the table you want to modify. metatable is the table to assign — pass nil to remove an existing metatable.

The function returns the table argument, so you can set the metatable and keep working in one expression.

Parameters

ParameterTypeDescription
tabletableThe table to attach the metatable to. In Lua 5.4+, this must be an actual table — setting a metatable on a non-table value like a number or string raises an error.
metatabletable or nilThe metatable to attach. Use nil to remove the current metatable.

Return Value

Returns table — the same table you passed in. This is useful for chaining.

Basic Example

local fallback = {
    __index = function(self, key)
        return "default:" .. key
    end
}

local t = {}
setmetatable(t, fallback)

print(t.name)       --> default:name
print(t.any_key)    --> default:any_key

The __index metamethod fires whenever you access a key that does not exist in the table. In this example, every missing key returns "default:<key>" instead of nil. This is the simplest form of metatable-driven behavior — one function that provides fallback values for an entire table without manually populating every possible key.

Since setmetatable returns the table, you can do the setup in a single expression:

local t = setmetatable({}, {
    __index = function(self, key) return "default:" .. key end
})

This inline style is common in module constructors and class factories, where you want to create an object, attach its metatable, and return it all in one go. The explicit two-step version reads more clearly for beginners, but the chained form is idiomatic in production Lua code because it avoids an extra local variable.

Removing a metatable

Pass nil to strip the metatable:

local meta = {}
local t = {}
setmetatable(t, meta)

print(getmetatable(t) == meta)  --> true

setmetatable(t, nil)
print(getmetatable(t) == nil)    --> true

Removing a metatable with setmetatable(t, nil) is the standard way to strip all custom behavior from a table. Afterward, the table behaves like a plain table — no __index fallback, no operator overloading, no custom finalizers. This fails silently if the metatable has a __metatable lock (see below), so you should always verify with getmetatable after removal if the lock status is unknown.

Protected metatables

If a metatable has a __metatable field, setmetatable cannot replace it. The call does nothing — it returns the table but the old metatable stays:

local protected = {
    __metatable = "locked",
    __index = function(self, k) return k end
}

local t = {}
setmetatable(t, protected)
setmetatable(t, {})  -- no effect
print(getmetatable(t) == protected)  --> true

The __metatable value can be any Lua value. When getmetatable is called on a table whose metatable has this field, it returns the __metatable value rather than the actual metatable. This provides a privacy mechanism — library authors use it to hide implementation details while still exposing a readable label or version string. Use getmetatable to check before you try to change a metatable, since setmetatable(t, nil) will be silently ignored on a protected table.

Real-world example: vector type

Here’s how you might build a vector class with metatable behavior:

local function newVector(x, y)
    return setmetatable({
        x = x,
        y = y
    }, {
        __add = function(a, b)
            return newVector(a.x + b.x, a.y + b.y)
        end,
        __tostring = function(self)
            return "(" .. self.x .. ", " .. self.y .. ")"
        end,
        __index = {
            magnitude = function(self)
                return math.sqrt(self.x ^ 2 + self.y ^ 2)
            end
        }
    })
end

local v = newVector(3, 4)
print(v:magnitude())           --> 5.0
print(v + newVector(1, 1))     --> (4, 5)

Common Metamethods

These are the metamethods developers use most often:

MetamethodWhen it firesTypical use
__indext[key] on a missing keyDefault values, inheritance
__newindext[key] = value on a missing keyControlled writes, read-only tables
__add+ operatorVector addition, combining game objects
__callt()Making objects callable
__tostringprint(t) / tostring(t)Human-readable debug output

See Also