🎓 A1Learner
🔍
0 of 49 lessons completed

Lua Protected Metatables

A protected metatable hides the real metatable from getmetatable. getmetatable returns the protection value instead.

getmetatable returns the protection value

The metatable carries __metatable set to locked. getmetatable returns locked, not the real metatable.

local t = {}
local mt = {__metatable = "locked"}
setmetatable(t, mt)
print(getmetatable(t))

rawget skips __index

__index supplies x from a fallback table. rawget looks only in the table itself, so it finds nothing.

local fallback = {x = 99}
local t = setmetatable({}, {__index = fallback})
print(t.x)
print(rawget(t, "x") == nil)

rawset skips __newindex

__newindex logs every normal write instead of storing it. A raw write stores the value directly.

local log = {}
local mt = {__newindex = function(tbl, k, v) log[#log + 1] = k end}
local t = setmetatable({}, mt)
t.a = 1
rawset(t, "b", 2)
print(rawget(t, "a") == nil)
print(rawget(t, "b"))
print(#log)

Example

local fallback = {score = 50}
local t = {}
local mt = {}
mt.__metatable = "locked"
mt.__index = fallback
mt.__newindex = function() end
mt.__pairs = function()
  local done = false
  return function()
    if not done then
      done = true
      return "custom", 7
    end
  end
end
setmetatable(t, mt)
print(getmetatable(t))
print(t.score)
rawset(t, "score", 80)
print(rawget(t, "score"))
for k, v in pairs(t) do
  print(k, v)
end
print(next(t))

Key points

  • __metatable: __metatable is a field in a metatable that sets the protection value.
  • getmetatable: getmetatable on a protected table returns the protection value, not the real metatable.
  • setmetatable: replacing the metatable of a protected table fails.
  • rawget: rawget reads a table field without calling any metamethod.
  • rawset: rawset writes a table field without calling any metamethod.
  • Bypass: raw functions bypass metamethods, including __index and __pairs.

In this example, getmetatable returns locked for the protected table. Reading score uses __index and gives 50, while rawset stores 80 directly. The pairs walk yields the custom key, but next walks the raw table and gives score.

Note

protection only changes what getmetatable returns, so raw access to the table still works.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

getmetatable returns "locked" for a table. What can you conclude?