Metatables

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Metatables permitem que tabelas se tornem mais poderosas do que antes. Elas estão anexadas a dados e contêm valores chamados metamétodos. Metamétodos são acionados quando uma certa ação é utilizada com o dado ao qual estão anexados.

Manipular metatables

As duas funções principais para adicionar e encontrar a metatable de uma tabela são setmetatable() e getmetatable().

local x = {}
local metaTable = {} -- Metatables também são tabelas!
setmetatable(x, metaTable) -- Dê a x uma metatable chamada metaTable!
print(getmetatable(x)) --> table: [endereço de memória hexadecimal]

A função setmetatable() também retorna a tabela da qual você está definindo a metatable, então esses dois scripts fazem a mesma coisa:

local x = {}
setmetatable(x, {})
local x = setmetatable({}, {})

Metamétodos

Metamétodos são as funções que são armazenadas dentro de uma metatable. Eles podem ir de chamar uma tabela, a adicionar uma tabela, até mesmo dividir tabelas. Aqui está a lista de metamétodos disponíveis:

MétodoDescrição
__index(table, index)Acionado quando table[index] é indexado, se table[index] for nil. Também pode ser definido como uma tabela, caso em que essa tabela será indexada.
__newindex(table, index, value)Acionado quando table[index] tenta ser definido (table[index] = value), se table[index] for nil. Também pode ser definido como uma tabela, caso em que essa tabela será indexada.
__call(table, ...)Acionado quando a tabela é chamada como uma função, ... são os argumentos que foram passados.
__concat(table, value)Acionado quando o operador de concatenação .. é usado na tabela.
__unm(table)Acionado quando o operador unário é usado na tabela.
__add(table, value)O operador de adição +.
__sub(table, value)O operador de subtração .
__mul(table, value)O operador de multiplicação *.
__div(table, value)O operador de divisão /.
__idiv(table, value)O operador de divisão inteira //.
__mod(table, value)O operador de módulo %.
__pow(table, value)O operador de exponenciação ^.
__tostring(table)Acionado quando tostring é chamado na tabela.
__metatableSe presente, bloqueia a metatable para que getmetatable() retorne isso em vez da metatable e setmetatable() gere um erro. Valor não-funcional.
__eq(table, value)O operador de igualdade ==¹
__lt(table, value)O operador de menor que <¹
__le(table, value)O operador de menor ou igual <=¹
__modeUsado em tabelas fracas, declarando se as chaves e/ou valores de uma tabela são fracos. Veja Usar tabelas fracas.
__len(table)Acionado quando o operador de comprimento # é usado no objeto.
__iter(table)Usado para denotar um iterador personalizado ao usar iteração generalizada.

Deve-se notar que ao escrever funções para metamétodos aritméticos ou relacionais, os dois parâmetros de função são intercambiáveis entre a tabela que acionou o metamétodo e o outro valor. Por exemplo, ao fazer operações vetoriais com escalares, a divisão não é comutativa. Portanto, se você estivesse escrevendo metamétodos para sua própria classe vector2, você deve ter cuidado para considerar ambos os cenários.

local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a é um escalar, b é um vetor
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a é um vetor, b é um escalar
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- tanto a quanto b são vetores
return vector2.new(a.x / b.x, a.y / b.y)
end
end
function mt.__tostring(t)
return t.x .. ", " .. t.y
end
function vector2.new(x, y)
local self = setmetatable({}, mt)
self.x = x or 0
self.y = y or 0
return self
end
local a = vector2.new(10, 5)
local b = vector2.new(-3, 4)
print(a / b) -- -3.3333333333333, 1.25
print(b / a) -- -0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5

Uso

Existem muitas maneiras de usar metatables.

Aplicar o operador de menos unário a tabelas

O metamétodo __unm lida com o operador de menos unário. Use-o para negar cada valor em uma tabela:

local metatable = {
__unm = function(t) -- __unm é para o operador unário -
local negated = {}
for key, value in t do
negated[key] = -value -- nega todos os valores nesta tabela
end
return negated -- retorna a tabela
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12

Indexar valores ausentes de outra tabela

Aqui está uma maneira interessante de declarar coisas usando __index:

local metatable = {
__index = {x = 1}
}
local t = setmetatable({}, metatable)
print(t.x) --> 1

__index foi acionado quando x foi indexado na tabela e não encontrado. Luau então procurou na tabela __index por um índice chamado x, e, ao encontrar um, retornou isso.

Chamar uma tabela como uma função

Metatables podem fazer muito mais do que fornecer um valor ausente. Veja este exemplo:

local t = {10, 20, 30}
print(t(5))

Normalmente você não pode chamar uma tabela, mas com metatables você pode:

local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Adiciona o argumento (5) ao valor, depois coloca na nova tabela (t).
end
return unpack(sum) -- Retorna os valores individuais da tabela
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35

Aplicar o operador de adição a tabelas

Você pode fazer muito mais também, como adicionar tabelas:

local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
for k, v in table1 + table2 do
print(k, v)
end

Isso gerará um erro dizendo que você está tentando realizar aritmética em uma tabela, mas funciona quando tentado com uma metatable:

local metatable = {
__add = function(t1, t2)
local sum = {}
for key, value in t1 do
sum[key] = value
end
for key, value in t2 do
if sum[key] then
sum[key] += value
else
sum[key] = value
end
end
return sum
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
local table2 = setmetatable({13, 14, 15}, metatable)
for k, v in table1 + table2 do
print(k, v)
end

Definir valores sem invocar metamétodos

Ao brincar com metatables, você pode encontrar alguns problemas. Se você precisar usar o metamétodo __index para criar novos valores em uma tabela, mas a metatable dessa tabela também tiver um metamétodo __newindex, você vai querer usar a função interna do Luau rawset() para definir o valor sem invocar nenhum metamétodo. Veja o seguinte código como um exemplo do que acontece se você não usar essas funções.

local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Não defina valores na tabela da maneira normal
end
})
print(t[1]) -- Causa um estouro de pilha

Estouros de pilha acontecem quando você tenta chamar uma função de si mesma muitas vezes. Na função __index acima, self[i] é definido para um valor, então quando chega à próxima linha, self[i] deve existir e presumivelmente não chamará o metamétodo __index. O problema é que __newindex não permite que você defina o valor. Sua presença impede que valores sejam adicionados à tabela com o método padrão t[i] = v. Para contornar isso, use a função rawset():

local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Não defina valores na tabela da maneira normal
end
})
print(t[1]) --> 10

Usar tabelas fracas

Variáveis que contêm tabelas, funções, threads e userdata, incluindo Instances, armazenam referências a esses objetos em vez de cópias deles. Atribuir um objeto a outra variável cria outra referência ao mesmo objeto:

local map = {key = 1}
local mapReference = map
print(map == mapReference) --> true
print(map == {key = 1}) --> false
local function func() end
local funcReference = func
print(func == funcReference) --> true
local part = Instance.new("Part")
local partReference = part
print(part == partReference) --> true

Objetos que não são mais acessíveis através de referências fortes se tornam elegíveis para coleta de lixo. Definir uma variável como nil não torna um objeto elegível para coleta de lixo se outra referência forte ainda aponta para esse objeto:

local child = {}
local parent = {Child = child}
child = nil
print(parent.Child) --> table: [endereço de memória hexadecimal]

Embora child não se refira mais à tabela child, parent.Child ainda se refere a ela. Enquanto a tabela pai permanecer acessível, sua referência mantém a tabela child acessível também.

Uma tabela fraca mantém chaves, valores ou ambos sem impedir que o coletor de lixo recupere objetos que de outra forma seriam inacessíveis. Defina o campo __mode da metatable da tabela para controlar quais referências são fracas:

  • "k" torna as chaves fracas.
  • "v" torna os valores fracos.
  • "kv" ou "vk" torna tanto as chaves quanto os valores fracos.

O seguinte exemplo de código cria uma tabela com valores fracos:

local children = setmetatable({}, {
__mode = "v",
})
do
local child = {}
children.example = child
end

Depois que o bloco do termina, o valor armazenado em children.example não tem referências fortes e se torna elegível para coleta de lixo. Quando o coletor de lixo recupera a tabela child, Luau remove sua entrada de children. A coleta de lixo é não determinística, então não dependa de quando a entrada desaparece.

Qualquer modo que você deixar de fora permanece forte. Chaves em uma tabela __mode = "v" permanecem referências fortes, então um objeto armazenado como uma chave permanece acessível e sua entrada permanece na tabela.

Para Instances, a coleta de lixo se aplica à referência Luau em vez de ao objeto em si. O mecanismo armazena dados de instância fora da VM Luau, então coletar uma referência Luau não exclui a instância, e uma instância que permanece no modelo de dados não mantém sua referência Luau viva. Para liberar uma instância, chame Instance:Destroy() e limpe todas as referências restantes a ela.

Usar o tipo de dado conjunto

Um conjunto é uma coleção de itens sem ordem e sem elementos duplicados. Um item está ou não está contido dentro de um conjunto. Usando metatables, você pode construir e manipular conjuntos dentro de scripts Luau.

Métodos básicos

O seguinte código inclui funcionalidade básica de conjunto, permitindo que você construa novos conjuntos, adicione e remova um item, verifique se um conjunto contém um item e exiba o conteúdo de um conjunto.

local Set = {}
Set.__index = Set
-- Função para construir um conjunto a partir de uma lista opcional de itens
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Função para adicionar um item a um conjunto
function Set:add(item)
self[item] = true
end
-- Função para remover um item de um conjunto
function Set:remove(item)
self[item] = nil
end
-- Função para verificar se um conjunto contém um item
function Set:contains(item)
return self[item] == true
end
-- Função para exibir o conjunto como uma lista delimitada por vírgulas para depuração
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end

Criar conjunto

Um novo conjunto pode ser construído chamando Set.new() com um array opcional de itens a serem adicionados.

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})

Note que, por definição, um conjunto não tem conceito de ordenação.

Adicionar item

Adicionar um item a um conjunto existente pode ser feito através do método Set:add().

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:add("Mango")

Remover item

Para remover um item de um conjunto, chame Set:remove() com o nome do item.

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:remove("Orange")

Verificar item

Para verificar se um conjunto contém um item específico, use Set:contains().

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local result1 = fruits:contains("Cherry")
print(result1) -- true
local result2 = fruits:contains("Watermelon")
print(result2) -- false

Métodos adicionais

Outras operações úteis podem ser implementadas para conjuntos, permitindo que você compare itens entre conjuntos, combine conjuntos ou subtraia um conjunto de outro.

Interseção

Ao considerar conjuntos como diagramas de Venn, você pode obter a interseção de dois conjuntos da seguinte forma, significando os itens que aparecem em ambos os conjuntos.

local function getIntersection(set1, set2)
local result = Set.new()
for key, value in set1 do
if set2:contains(key) then
result:add(key)
end
end
return result
end
local freshFruits = Set.new({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, Peach

União

Você pode obter a união de dois conjuntos com a seguinte função, significando uma coleção dos itens em ambos os conjuntos sem duplicatas. Note que esta função usa o método __add da metatable para fornecer um atalho de adição de set1 + set2.

function Set:__add(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = true
end
return result
end
local sweetFruits = Set.new({"Apple", "Mango", "Cherry", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- Peach, Lime, Apple, Cherry, Lemon, Mango

Subtração

Você pode remover todos os itens em um conjunto dos itens em outro conjunto através da seguinte função. Semelhante à função acima, isso usa o método __sub da metatable para fornecer um atalho de subtração de set1 - set2.

function Set:__sub(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = nil
end
return result
end
local allFruits = Set.new({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Apple, Cherry, Peach
©2026 Roblox Corporation, Roblox, o logotipo Roblox e Powering Imagination estão entre nossas marcas registradas e não registradas nos EUA e em outros países.