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