{}const=>[]async()letfn</>var
LuaDesarrollo

Metatables en Lua: lo complejo en términos sencillos

Veamos qué son las metatables en Lua, por qué son necesarias y cómo usarlas para añadir «superpoderes» a las tablas.

К

Kodik

Autor

4 min de lectura

A menudo queremos que las tablas se comporten de una «manera especial»: que se sumen como vectores, que sustituyan valores predeterminados, que se impriman de forma bonita. ✨ Para ello, Lua tiene metatables - guionistas ocultos del comportamiento de las tablas. A continuación se muestra un análisis breve, práctico y sencillo.

🧩 ¿Qué son las metatables?

Metatable — es una tabla normal adjunta a otra tabla a través de setmetatable(), que describe cómo responde un objeto a las operaciones: indexación, suma, comparación, concatenación, llamada, etc.

Piensa en la metatabla como una «capa de reglas»: Lua comprueba las claves y las operaciones y, si encuentra el metamétodo correspondiente (por ejemplo, __index), lo utiliza.

🧰 ¿Por qué es necesario?

Tarea

Metamétodo

Valores predeterminados / prototipo

__index

Control/prohibición de grabación

__newindex

Redefinición de la suma/multiplicación/...

__add, __mul, …

Una bonita conclusión

__tostring

Comparación / longitud / concatenación

__eq, __lt, __len, __concat

Hacemos la tabla "llamada"

__call

Protegemos la metatabla

__metatable

🔥 100.000+ estudiantes ya están con nosotros

¿Cansado de leer teoría?
¡Hora de programar!

Kodik — una app donde aprendes a programar con práctica. Mentor IA, lecciones interactivas, proyectos reales.

🤖 IA 24/7
🎓 Certificados
💰 Gratis
🚀 Empezar
Se unieron hoy

El ejemplo más corto: sumar tablas como vectores

local mt = {
  __add = function(a, b)
    return { a[1] + b[1], a[2] + b[2] }
  end
}

local v1 = setmetatable({1, 2}, mt)
local v2 = setmetatable({3, 4}, mt)

local sum = v1 + v2
print(sum[1], sum[2]) --> 4  6

Sin metatablas v1 + v2 causaría un error: Lua no sabe cómo añadir tablas.

🧭 __index: valores predeterminados y «herencia»

__index se activa cuando no hay clave en la tabla. Se puede establecer mediante una referencia a una tabla prototipo o una función.

Opción 1: prototipo

local defaults = { speed = 10, hp = 100 }
local player = setmetatable({}, { __index = defaults })

print(player.speed)  --> 10 (берётся из defaults)
player.speed = 20    -- теперь свой speed у player
print(player.speed)  --> 20

Opción 2: creación automática de valores (inicialización perezosa)

local counts = setmetatable({}, {
  __index = function(t, key)
    local v = 0
    rawset(t, key, v)     -- записываем сразу, чтобы дальше было «как будто существовало всегда»
    return v
  end
})

counts["apples"] = counts["apples"] + 1
print(counts["apples"]) --> 1

Usamos rawset para no llamar a __newindex y no entrar en una recursión.

🛡️ __newindex: control de grabación

local guarded = setmetatable({ x = 0, y = 0 }, {
  __newindex = function(t, k, v)
    if k == "x" or k == "y" then
      rawset(t, k, v)              -- безопасная запись без повторного вызова __newindex
    else
      error("You cannot add a new field: " .. tostring(k), 2)
    end
  end
})

guarded.x = 10    -- ок
guarded.title = "oops"  -- ошибка

Un error común es escribir t[k] = v dentro de __newindex. Esto volverá a llamar a __newindex y dará lugar a una recursión. Utilice rawset.

⚠️ Trampas

  • Metatablas no se copian al copiar una tabla de forma superficial, vuelva a colgarla.

  • Las cadenas demasiado astutas __index hacen que la depuración sea más difícil. Mantenga el modelo simple.

  • rawget/rawset los metamétodos de omisión son tanto una fortaleza como un riesgo. Úselos con cuidado.

🚀 Técnicas avanzadas de Metatables

📞 __call: creamos una «fábrica» de objetos

El metamétodo __call permite llamar a la tabla como una función, lo cual es conveniente para fábricas y DSL.

local greeter = setmetatable({}, {
  __call = function(_, name) print("Hi, " .. name .. "!") end
})

greeter("Code") --> Привет, Кодик!

🔒 __metatable: protección contra cambios

Puedes ocultar los metamétodos reales y prohibir su cambio de la siguiente manera:

local obj = {}
local mt  = { __metatable = "Access to the metatable is denied" }
setmetatable(obj, mt)

print(getmetatable(obj)) --> Доступ к метатаблице запрещён
-- getmetatable(obj).__index  -- уже не получить

🎨 Bonita salida y operadores «mágicos»

local mt = {
  __tostring = function(t) return ("Point(%d, %d)"):format(t.x, t.y) end,
  __len      = function(t) return math.sqrt(t.x*t.x + t.y*t.y) end, -- длина вектора через #
  __eq       = function(a,b) return a.x==b.x and a.y==b.y end,
  __add      = function(a,b) return setmetatable({x=a.x+b.x, y=a.y+b.y}, mt) end,
}
local p = setmetatable({x=3, y=4}, mt)
print(p)    --> Point(3, 4)
print(#p)   --> 5

💬 ¿Dónde aplicarías las metatables en tu juego de Roblox/Love2D, en el sistema de configuración o en mini-DSL?

📚 ¿Quieres profundizar en el tema?

En el anexo Kodik encontrarás tutoriales detallados sobre Lua, ejercicios paso a paso, análisis de errores y práctica cómoda directamente en tu teléfono o navegador.

Y si quieres estar al tanto de las noticias, nuevas características y materiales útiles, suscríbete a nuestro Canal de Telegram. Es acogedor, profesional y con amor por el código ❤️

🎯Deja de postergar

¿Te gustó el artículo?
¡Hora de practicar!

En Kodik no solo lees — escribes código de inmediato. Teoría + práctica = habilidades reales.

Práctica instantánea
🧠IA explica código
🏆Certificado

Sin registro • Sin tarjeta