Read Only Tables |
|
We can define read-only (constant) tables like this:
Directions = readonlytable { LEFT = 1, RIGHT = 2, UP = 3, DOWN = 4 otherstuff = {} }
if we define the readonlytable
helper functions as follows:
function readonlytable(table) return setmetatable({}, { __index = table, __newindex = function(table, key, value) error("Attempt to modify read-only table") end, __metatable = false }); end
Note that readonlytable
does not return the table originally passed to it but rather a proxy table. The proxy table is given a metatable with the metamethods __index
and __newindex
to ensure that the proxy table values never change. Setting the __metatable
metamethod prevents tampering with the metatable itself--the client cannot obtain nor change the metatable via getmetatable
and setmetatable
functions.
Now if we try to modify any member of Directions
we will get an error.
> Directions.LEFT = 33
Attempt to modify read-only table
Although members of the read-only table cannot be changed, it is still possible to modify members of members of read-only tables (unless they too are explicitly made read-only tables):
> Directions.otherstuff = nil -- will fail Attempt to modify read-only table > Directions.otherstuff.foo = 1 -- allowed
Also, rawset()
and table.insert
can still be used to directly modify a read-only table:
rawset(Directions, "LEFT", 5) print(Directions.LEFT) -- prints 5 table.insert(Directions, 6) print(Directions[1]) -- prints 6
If you really need to avoid that, you could implement the read-only table in C.
Furthermore, this method of creating read-only tables interferes with pairs
, ipairs
, next
, the #
operator, and other forms of table iteration. For example,
-- prints nothing! for k,v in pairs(Directions) do print(k,v) end print(next(Directions)) -- prints nil! print(#Directions) -- prints "0"!
See also GeneralizedPairsAndIpairs for ways to handle that.
Original author: KevinBaca