Read Only Tables

lua-users home
wiki

This article describes a technique for making a table read-only via metamethods in Lua 5.0/5.1 (for background on metamethods see the MetamethodsTutorial). The approach will only prevent the inadvertent modification of any member of a read-only table.

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

See Also


RecentChanges · preferences
edit · history
Last edited May 28, 2007 9:05 pm GMT (diff)