El metamétodo __newindex
Parte de la sección Lógica y flujo del Journey de Lua de Coddy. Lección 19 de 54.
Has aprendido cómo __index controla lo que ocurre cuando intentas leer una clave que no existe en una tabla. Ahora descubrirás su contraparte: __newindex, que controla lo que ocurre cuando asignas un valor a una clave que la tabla todavía no tiene.
El metamétodo __newindex se activa cada vez que intentas asignar un valor a una clave que no existe actualmente en la tabla. Esto te permite interceptar y controlar las modificaciones antes de que se realicen.
Aquí tienes un ejemplo sencillo que impide que se añadan claves nuevas a una tabla:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Salida: Cannot add key: name
print(readOnly.name) -- Salida: nilCuando intentas establecer readOnly.name = "Alice", Lua ve que name no existe en la tabla. En lugar de añadirlo, Lua llama a la función __newindex con tres argumentos: la tabla misma, la clave que se está estableciendo y el valor que se está asignando.
En este ejemplo, la función simplemente imprime un mensaje y en realidad no almacena nada.
También puedes usar __newindex para validar los datos antes de permitir que se añadan a tu tabla:
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- Salida: Health must be a number!
player.health = 100 -- Permitido, almacenado con rawset
player.health = "high" -- Sin mensaje: health ya existeObserva el uso de rawset() en el segundo ejemplo. Esta función omite la metatabla y establece directamente el valor en la tabla.
Sin él, establecer el valor volvería a activar __newindex, creando un bucle infinito.
Como __newindex solo se ejecuta para las claves que la tabla no tiene, esta comprobación protege la primera asignación de cada clave. Una vez que rawset ha almacenado health, las asignaciones posteriores la cambian directamente. Para comprobar cada asignación, mantén vacía la tabla y almacena los valores en una segunda tabla, a la que accede mediante __index:
local data = {}
local player = setmetatable({}, {
__index = data,
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
data[key] = value
end
end
})
player.health = 100
player.health = "high" -- Salida: Health must be a number!
print(player.health) -- Salida: 100player nunca obtiene una clave propia, así que cada asignación pasa por __newindex, y cada lectura recurre a __index para acceder a data.
Desafío
FácilEscribe una función createValidatedTable que reciba key y value y devuelva una tabla que solo acepte valores de cadena para las claves nuevas.
Crea una tabla vacía y asígnale una metatabla con __newindex que valide el tipo del valor. Si alguien intenta agregar un valor que no sea una cadena, usa rawset() para almacenar la cadena "invalid" en su lugar. Para los valores de cadena, almacénalos normalmente usando rawset().
Lógica:
- Crea una tabla vacía
- Crea una metatabla con
__newindexque reciba la tabla, la clave y el valor - Dentro de
__newindex, comprueba si el valor es una cadena usandotype(value) == "string" - Si es una cadena, usa
rawset(t, key, value)para almacenarla - Si no es una cadena, usa
rawset(t, key, "invalid")para almacenar"invalid"en su lugar - Asigna la metatabla a la tabla usando
setmetatable() - Usa
rawset()para agregar el par inicial clave-valor a la tabla (omitiendo la metatabla) - Devuelve la tabla
Parámetros:
key(cadena): La clave inicial que se agregará a la tablavalue(cadena): El valor inicial que se agregará a la tabla
Devuelve: Una tabla con validación que solo acepta valores de cadena y que contiene el par inicial clave-valor (tabla)
Pruébalo tú mismo
function createValidatedTable(key, value)
-- Escribe el código aquí
end
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Lógica y flujo
1Iteración avanzada de tablas
Iterar con pairs()Iterar con ipairs()pairs() vs. ipairs()Resumen - Ficha de personaje4Introducción a las Metatablas
¿Qué es una Metatabla?setmetatable y getmetatableEl metamétodo __indexEl metamétodo __newindexEl metamétodo __tostringMetamétodos aritméticos - Parte 1Metamétodos aritméticos - Parte 2Resumen - Tablas de solo lectura2Más funciones de la librería table
table.concat()Construcción de tablas y unpack()table.sort()Ordenamiento personalizado con funcionesRepaso - Tabla de puntuaciones altasPractica por tu cuenta: Compilador de Lua online