O Metamétodo __newindex
Parte da seção Lógica e fluxo do Journey de Lua da Coddy. Lição 19 de 54.
Você aprendeu como __index controla o que acontece quando você tenta ler uma chave que não existe em uma tabela. Agora vai descobrir sua contraparte: __newindex, que controla o que acontece quando você atribui um valor a uma chave que a tabela ainda não tem.
O metamétodo __newindex é acionado sempre que você tenta atribuir um valor a uma chave que não existe atualmente na tabela. Isso dá a você o poder de interceptar e controlar as modificações antes que elas aconteçam.
Aqui está um exemplo simples que impede que novas chaves sejam adicionadas a uma tabela:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Saída: Cannot add key: name
print(readOnly.name) -- Saída: nilQuando você tenta definir readOnly.name = "Alice", Lua percebe que name não existe na tabela. Em vez de adicioná-lo, Lua chama a função __newindex com três argumentos: a própria tabela, a chave que está sendo definida e o valor que está sendo atribuído.
Neste exemplo, a função simplesmente imprime uma mensagem e não armazena nada de verdade.
Você também pode usar __newindex para validar os dados antes de permitir que sejam inseridos na sua tabela:
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" -- Saída: Health must be a number!
player.health = 100 -- Permitido, armazenado com rawset
player.health = "high" -- Nenhuma mensagem: health já existeObserve o uso de rawset() no segundo exemplo. Essa função ignora a metatabela e define diretamente o valor na tabela.
Sem isso, definir o valor acionaria __newindex novamente, criando um loop infinito.
Como __newindex só é executado para chaves que a tabela não possui, essa verificação protege a primeira atribuição de cada chave. Depois que rawset armazena health, as atribuições seguintes o alteram diretamente. Para verificar todas as atribuições, mantenha a própria tabela vazia e armazene os valores em uma segunda tabela, que ela consulta por meio de __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" -- Saída: Health must be a number!
print(player.health) -- Saída: 100player nunca recebe uma chave própria, então toda atribuição passa por __newindex, e toda leitura recorre a __index para acessar data.
Desafio
FácilEscreva uma função createValidatedTable que recebe key e value e retorna uma tabela que aceita apenas valores string para novas chaves.
Crie uma tabela vazia e associe a ela uma metatabela com __newindex que valida o tipo do valor. Se alguém tentar adicionar um valor que não seja string, use rawset() para armazenar a string "invalid" no lugar. Para valores string, armazene-os normalmente usando rawset().
Lógica:
- Crie uma tabela vazia
- Crie uma metatabela com
__newindexque recebe a tabela, a chave e o valor - Dentro de
__newindex, verifique se o valor é uma string usandotype(value) == "string" - Se for uma string, use
rawset(t, key, value)para armazená-la - Se não for uma string, use
rawset(t, key, "invalid")para armazenar"invalid"no lugar - Associe a metatabela à tabela usando
setmetatable() - Use
rawset()para adicionar o par inicial de chave e valor à tabela (ignorando a metatabela) - Retorne a tabela
Parâmetros:
key(string): A chave inicial a ser adicionada à tabelavalue(string): O valor inicial a ser adicionado à tabela
Retorno: Uma tabela com validação que aceita apenas valores string e contém o par inicial de chave e valor (tabela)
Experimente você mesmo
function createValidatedTable(key, value)
-- Escreva o código aqui
end
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Lógica e fluxo
1Iteração avançada de tabelas
Iterando com pairs()Iterando com ipairs()pairs() vs. ipairs()Revisão - Ficha de personagem2Mais funções da biblioteca table
table.concat()Construção de tabelas e unpack()table.sort()Ordenação personalizada com funçõesRevisão - Placar de pontuações máximasPratique por conta própria: Compilador de Lua online