Lua - Controlling Access to a Table
Lua metatable has two specific fields __index and __newindex to control table access. In this chapter, we're covering both the fields with metamethods with examples.
Using __index metamethod
__index metamethod is activated or invoked by Lua when we try to access a key in a table which is not present. We can use __index metamethod for −
Default Values − We can return a different default value instead of returning nil when a key is missing in the target table.
Delegation − We can forward the key lookup to other table.
Lazy Loading − We can load values when first accessed instead of load time to improve performance.
Virtual Property − We can compute values dynamically based on provided key instead of looking up stored data.
Example - Setting default values using __index
main.lua
-- Default values
local defaults = { width = 100, height = 50 }
-- main table
local shape = {}
-- metatable
local meta = { __index = defaults }
-- set metatable
setmetatable(shape, meta)
-- get default values of keys
-- prints 100
print(shape.width)
-- prints 50
print(shape.height)
-- prints nil, as not present in defaults
print(shape.color)
Output
When we run the above program, we will get the following output−
100 50 nil
Example - Delegation using __index
main.lua
-- main table
local shape = { width = 100, height = 50 }
-- proxy table
local proxy = {}
-- metatable to delegate to main table
local meta = { __index = shape }
-- set metatable to proxy
setmetatable(proxy, meta)
-- get value from main table via proxy
print(proxy.width)
-- set a value to proxy, but not to main table
proxy.color = "red"
-- try to get color from shape, prints nil
print(shape.color)
Output
When we run the above program, we will get the following output−
100 nil
Using __newindex metamethod
__newindex metamethod is activated or invoked by Lua when we try to assign a new key to the table. We can use __newindex metamethod for −
Read Only Table − We can make a table readonly by not allowing any new entry to it.
Validation − We can perform validation on the value assigned as required type of value or range of values.
Side Effects − We can perform actions as side effects while setting values like logging, tracing etc.
Delegation − We can even assign value to different table or object.
Example - Delegation using __newindex
main.lua
-- main table
local config = { max_users = 10 }
-- set metatable to make table readonly
-- no new entry is allowed
setmetatable(config, { __newindex = function(table, key, value)
error("No new entry allowed.", 2)
end })
-- this will throw an error
config.max_connections = 15
Output
When we run the above program, we will get the following output−
lua: main.lua:11: No new entry allowed.
stack traceback:
[C]: in function 'error'
main.lua:7: in metamethod 'newindex'
main.lua:11: in main chunk
[C]: in ?
Example - Validation using __newindex
main.lua
-- main table
local settings = {}
-- set a metatable to validate inputs to be numbers only
setmetatable(settings, { __newindex = function(table, key, value)
if type(value) == "number" then
rawset(table, key, value)
else
error("Value for " .. key .. " must be a number", 2)
end
end })
-- set a valid value
settings.volume = 75
print(settings.volume)
-- setting string throws an error
settings.base = "high"
Output
When we run the above program, we will get the following output−
75
lua: main.lua:16: Value for base must be a number
stack traceback:
[C]: in function 'error'
main.lua:9: in metamethod 'newindex'
main.lua:16: in main chunk
[C]: in ?