0Pricing
Lua Academy · Aula

__index e __newindex

Intercepte leituras e escritas de campos com os metamétodos __index e __newindex.

__index e __newindex é uma aula grátis de Lua Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Lua Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Lua Academy inclui 4 aulas no total.

Recapitulação de __index

__index é acionado quando o Lua tenta ler uma chave que não existe na tabela. Ele pode ser uma tabela (busca no protótipo) ou uma função (cálculo dinâmico). Este é o metamétodo mais usado no Lua — ele viabiliza herança, valores padrão e inicialização tardia.

local defaults = {timeout=30, retries=3}
local config = setmetatable({timeout=60}, {__index=defaults})

print(config.timeout)   -- 60 (own value)
print(config.retries)   -- 3  (from defaults)
print(config.missing)   -- nil (not in either)

__newindex Acionado

__newindex é acionado quando o Lua tenta escrever uma chave que ainda não existe na tabela. Se a chave já existir, a atribuição ocorrerá diretamente, sem acionar __newindex.

local proxy = setmetatable({}, {
  __newindex = function(t, k, v)
    print("New key:", k, "=", v)
    rawset(t, k, v)  -- actually store it
  end
})

proxy.x = 10    -- New key: x = 10
proxy.x = 20    -- no trigger! x already exists
print(proxy.x)  -- 20

Tabelas Somente para Leitura

Use __newindex para criar tabelas somente para leitura. Intercepte todas as escritas e gere um erro. Combine-o com __index para fornecer valores sem armazená-los na própria tabela.

local function readOnly(t)
  return setmetatable({}, {
    __index = t,
    __newindex = function(_, k, _)
      error("attempt to write to read-only table key: " .. tostring(k), 2)
    end
  })
end

local cfg = readOnly({host="localhost", port=8080})
print(cfg.host)    -- localhost
-- cfg.host = "x"  -- ERROR: attempt to write to read-only table

Acompanhando Alterações

Use __newindex para acompanhar todas as alterações em uma tabela — algo útil para depuração, registro de alterações ou vinculações de dados reativas. Armazene os dados em uma tabela oculta de suporte e registre as alterações em um log.

local log = {}
local data = {}
local tracked = setmetatable({}, {
  __index = data,
  __newindex = function(_, k, v)
    log[#log+1] = {key=k, old=data[k], new=v}
    data[k] = v
  end
})

tracked.name = "Alice"
tracked.age = 30
tracked.name = "Bob"

for _, entry in ipairs(log) do
  print(entry.key, entry.old, "->", entry.new)
end

Objetos Proxy

Um proxy envolve outro objeto para interceptar todas as leituras e escritas. Use uma tabela vazia como proxy, com __index e __newindex apontando para funções. Os dados reais ficam em uma tabela separada.

local function makeProxy(target)
  return setmetatable({}, {
    __index = function(_, k) return target[k] end,
    __newindex = function(_, k, v)
      print("Setting", k, "on target")
      target[k] = v
    end,
  })
end

local real = {x=10}
local p = makeProxy(real)
print(p.x)   -- 10 (reads from real)
p.y = 20     -- Setting y on target
print(real.y) -- 20

Cadeia de __index (Herança)

Encadear metatabelas por meio de __index cria herança de protótipos. Quando uma chave não é encontrada no objeto, o Lua verifica a classe, depois a classe pai e assim por diante. Cada nível precisa de sua própria metatabela, com __index apontando para o próximo nível.

local Base  = {type = "base",  version = 1}
local Child = setmetatable({type = "child"}, {__index = Base})
local Obj   = setmetatable({}, {__index = Child})

print(Obj.type)     -- base? No: child (Child has it)
print(Obj.version)  -- 1 (from Base)
print(Obj.type)     -- child (Child overrides Base.type)

Inicialização Tardia com __index

Use um __index na forma de função para calcular e armazenar em cache valores dispendiosos no primeiro acesso. Na primeira leitura, calcule o valor e armazene-o diretamente na tabela (ignorando futuras invocações de __index).

local lazy = setmetatable({}, {
  __index = function(t, k)
    if k == "expensiveData" then
      print("Computing...")
      local result = {1,2,3,4,5}   -- simulate work
      rawset(t, k, result)
      return result
    end
  end
})

print(lazy.expensiveData)  -- Computing...  then table
print(lazy.expensiveData)  -- (no "Computing" this time)

__newindex para Validação de Esquema

Use __newindex para impor um esquema: permita apenas a definição de chaves predefinidas ou de valores do tipo correto. Isso adiciona segurança de tipos básica às tabelas do Lua.

local schema = {name="string", age="number", active="boolean"}

local function schemaTable()
  local store = {}
  return setmetatable({}, {
    __index = store,
    __newindex = function(_, k, v)
      local expected = schema[k]
      if not expected then error("unknown key: "..k,2) end
      if type(v) ~= expected then
        error(k.." must be "..expected..", got "..type(v),2)
      end
      store[k] = v
    end
  })
end

local user = schemaTable()
user.name = "Alice"
user.age  = 30
-- user.age = "thirty"  -- ERROR

__index versus rawget

Dentro de uma função __index, use rawget para ler da tabela sem acionar __index novamente (o que causaria recursão infinita). Sempre use rawget/rawset dentro dos metamétodos para manipular o armazenamento real da tabela.

local counter = setmetatable({count=0}, {
  __index = function(t, k)
    -- Use rawget to avoid recursion
    local c = rawget(t, "count")
    rawset(t, "count", c + 1)
    return rawget(t, k)
  end
})

print(counter.count)    -- 0 (direct, no __index)
print(counter.missing)  -- nil
print(counter.count)    -- 1 (incremented)

Combinando __index e __newindex

O padrão mais poderoso: use uma tabela proxy vazia com __index e __newindex, apoiada por uma tabela de dados oculta. Isso oferece controle total sobre leituras e escritas, permitindo recursos como detecção de alterações, carregamento tardio e controle de acesso.

local function observable(init)
  local data = init or {}
  local listeners = {}
  local obj = setmetatable({}, {
    __index = data,
    __newindex = function(_, k, v)
      local old = data[k]
      data[k] = v
      for _, cb in ipairs(listeners) do cb(k,old,v) end
    end
  })
  obj._onchange = function(_, cb) listeners[#listeners+1]=cb end
  return obj
end

local obs = observable({score=0})
obs:_onchange(function(k,o,n) print(k,o,"->",n) end)
obs.score = 100  -- score  0 -> 100

Verificação Rápida

Quando __newindex NÃO é acionado?

Recapitulação: __index e __newindex

Pontos principais:

  • __index: acionado ao ler uma chave ausente; pode ser tabela ou função
  • __newindex: acionado somente ao escrever uma chave nova
  • Use rawget/rawset dentro dos metamétodos para evitar recursão
  • Padrões: tabelas somente para leitura, acompanhamento de alterações, inicialização tardia, validação de esquema
  • Proxy vazio + tabela de suporte = interceptação completa de leituras e escritas

Perguntas Frequentes

A aula “__index e __newindex” é grátis?

Sim — o texto completo de “__index e __newindex” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Lua Academy, atualize para CoddyKit PRO. O curso de Lua Academy inclui 4 aulas no total.

O que vou aprender em “__index e __newindex”?

Intercepte leituras e escritas de campos com os metamétodos __index e __newindex. Você pratica Lua Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Lua Academy?

Nenhuma experiência prévia é necessária. Lua Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “__index e __newindex”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Lua Academy?

Sim. Cada aula de Lua Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Introdução às metatabelas
  2. __index e __newindex
  3. Metamétodos aritméticos
  4. __tostring e __len
← Voltar para Lua Academy