Protected table

how to create a protected table?
Example:

local c = game:GetService('ContextActionService') 
print(c) -- ContextActionService
print(getmetatable(c)) -- The metatable is locked
c.u = 'u' -- u is not a valid member of ContextActionService "ContextActionService"
print(c[1]) -- 1 is not a valid member of ContextActionService "ContextActionService"
local function LockTable(Table)
	local proxy = newproxy(true) -- creates blank userdata with metatable (the reason we use userdata is because if we used table, users would still be able to edit the table with the table library and rawget/rawset)
    local meta = getmetatable(proxy) -- gets metatable of blank userdata
	
	meta.__index = function(_, key)
		return Table[key] -- return the value from the "real table"
	end
	meta.__newindex = function(_, key, value)
         -- can leave the blank or throw an error, the point is that you don't set the  value to the real table in order to have it "locked"
	end
    meta.__metatable = "locked" -- lock metatable

	return proxy
end

local tab = LockTable({
     key = "value"
})

-- tests
print(tab.key) --> "value"
tab.key = "anothervalue"
print(tab.key) --> "value"
print(getmetatable(tab)) --> "locked"
table.insert(tab,"randomvalue") --> error: must pass table got userdata
1 Like

Protecting a table is possible through something called metamethods. Metamethods are methods that involve metatables, such as __index, __metatable, etcetra.

To “protect” a table from getmetatable, you would use the __metatable metamethod:

local x = {};
setmetatable(x, {
    __metatable = "This table is locked.";
});

print(getmetatable(x)); -- This table is locked.

To prevent new entries from being added, you would use the __newindex metamethod:

local x = {};
setmetatable(x, {
    __newindex = function(_table, _index, _value) print(_value, "is not a valid member of table."); return end;
});
x[1] = 2; -- 1 is not a valid member of table.

Combining the new yields:

local x = {};
setmetatable(x, {
    __metatable = "This table is locked.",
    __newindex = function(_table, _index, _value) print(_value, "is not a valid member of table."); return; end,
});

print(getmetatable(x)); -- This table is locked.
x[1] = 2; -- 1 is not a valid member of table.

You can learn more about metatables here.

You would still be able to edit existing keys in the table if you do that. The only way to do it is have a proxy table and use the __index metamethod to retrieve values from the “real table”.

The way to do it would be how I posted

Use the newly added table.freeze, it locks a table the same way Roblox would lock the string and math libraries. It’s better than using metamethods since they tend to be performance unfriendly.

EDIT: This doesn’t seem to be enabled. Also it doesn’t lock access to the metatable, you should still use __metatable for that

2 Likes