Menu
CoddyTech

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: nil

Quando 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á existe

Observe 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: 100

player nunca recebe uma chave própria, então toda atribuição passa por __newindex, e toda leitura recorre a __index para acessar data.

challenge icon

Desafio

Fácil

Escreva 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 __newindex que recebe a tabela, a chave e o valor
  • Dentro de __newindex, verifique se o valor é uma string usando type(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 à tabela
  • value (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
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de Lua online