Menu
Coddy logo textTech

O Metamétodo __newindex

Parte da seção Logic & Flow 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 você descobrirá sua contraparte: __newindex, que controla o que acontece quando você tenta adicionar ou modificar um par chave-valor.

O metamétodo __newindex é acionado sempre que você tenta atribuir um valor a uma chave que não existe atualmente na tabela. Isso lhe dá o poder de interceptar e controlar modificações antes que elas ocorram.

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", o Lua vê que name não existe na tabela. Em vez de adicioná-lo, o 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 fato.

Você também pode usar __newindex para validar dados antes de permiti-los em 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

Observe o uso de rawset() no segundo exemplo. Esta função ignora a metatable e define o valor diretamente na tabela.

Sem isso, definir o valor dispararia __newindex novamente, criando um loop infinito.

challenge icon

Desafio

Fácil

Escreva uma função createValidatedTable que recebe key e value e retorna uma tabela que aceita apenas valores do tipo string para novas chaves.

Crie uma tabela vazia e anexe um metatable 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" em seu lugar. Para valores do tipo string, armazene-os normalmente usando rawset().

Lógica:

  • Crie uma tabela vazia
  • Crie um metatable 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" em seu lugar
  • Anexe o metatable à tabela usando setmetatable()
  • Use rawset() para adicionar o par chave-valor inicial à tabela (ignorando o metatable)
  • Retorne a tabela

Parâmetros:

  • key (string): A chave inicial para adicionar à tabela
  • value (string): O valor inicial para adicionar à tabela

Retorna: Uma tabela com validação que aceita apenas valores string, contendo o par chave-valor inicial (table)

Folha de consulta

O metamétodo __newindex é acionado quando você tenta atribuir um valor a uma chave que não existe em uma tabela, permitindo interceptar e controlar modificações.

Exemplo básico impedindo novas chaves:

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

A função __newindex recebe três argumentos: a própria tabela, a chave que está sendo definida e o valor que está sendo atribuído.

Validando dados com __newindex:

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

Use rawset(t, key, value) para ignorar a metatabela e definir valores diretamente na tabela. Isso evita loops infinitos ao definir valores dentro de __newindex.

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 Logic & Flow