In the spirit of ReadOnlyTables, I needed a way to provide access control in a multi-user Lua system. Notably, it is a requirement that users have read-only access to complex data structures, and should not be able to modify them in any way.

It is a goal that even savvy Lua users should not be able to circumvent the protection.

__type and __pairs are set in each read-only table's metatable to support the cooresponding extensions to the standard library. Other than the metamethods, this module only exports ropairs, a version of pairs for read-only tables (which uses __pairs), and make_read_only, a constructor of read-only tables.

I would prefer to cache the read-only version of each table so as to avoid making redundant copies (and support equality testing of read-only tables) but as RiciLake points out at GarbageCollectingWeakTables, cacheing recursive data is problematic in Lua. Happily, read-only tables are rather lightweight, so this isn't as big as a problem as it could be.

In my implementation, I will probably modify the line local new={} in make_read_only to generate a new userdata instead, to properly catch attempts to treat a read-only table like a standard table (using, for instance, pairs or ipairs or rawset or table.insert.

I'm posting this early in hopes of getting some feedback. I need a solution to this problem, and this turned out to be easier to code in pure Lua than I had hoped. But any improvements would be welcomed.

Unfortunately, each access of a subtable returns a freshly-created read-only table. If a table is a key in a read-only table, you can't pull it out of the read-only table, but you can still use it as a key if you have access to it by other means.

I want to strengthen this to honor the wrapped table's __index and __pairs metatables, and hopefully come up with a caching strategy that doesn't break garbage collection. Maybe I'll post RecursiveReadOnlyTablesTwo? one day.

I saw your implementation and thought I would see if I could implement something that got around a few of the catches you were experiencing. Some of the catches in your case break the code in my case. One such case is that each subtable access returns a freshly-created read-only table. I also was afraid of breaking garbage collection since the objects that I want to make read-only are created and destroyed extremely often. I do not have the experience that I would like to working with metatables and these kinds of constructs so any advice or corrections would be much appreciated.

I am not sure how safe this is from an expert who is given a read-only table, but it seems that since the table is only ever referenced indirectly through a closure, it should be completely safe from damage. Also, as far as I can tell, this will not break garbage collection. Let me know what you think.