The __newindex Metamethod
Part of the Logic & Flow section of Coddy's Lua journey — lesson 19 of 54.
You've learned how __index controls what happens when you try to read a key that doesn't exist in a table. Now you'll discover its counterpart: __newindex, which controls what happens when you try to add or modify a key-value pair.
The __newindex metamethod is triggered whenever you attempt to assign a value to a key that doesn't currently exist in the table. This gives you the power to intercept and control modifications before they happen.
Here's a simple example that prevents any new keys from being added to a table:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Output: Cannot add key: name
print(readOnly.name) -- Output: nilWhen you try to set readOnly.name = "Alice", Lua sees that name doesn't exist in the table. Instead of adding it, Lua calls the __newindex function with three arguments: the table itself, the key being set, and the value being assigned.
In this example, the function simply prints a message and doesn't actually store anything.
You can also use __newindex to validate data before allowing it into your table:
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- Output: Health must be a number!
player.health = 100 -- AllowedNotice the use of rawset() in the second example. This function bypasses the metatable and directly sets the value in the table.
Without it, setting the value would trigger __newindex again, creating an infinite loop.
Challenge
EasyWrite a function createValidatedTable that takes key and value and returns a table that only accepts string values for new keys.
Create an empty table and attach a metatable with __newindex that validates the value type. If someone tries to add a non-string value, use rawset() to store the string "invalid" instead. For string values, store them normally using rawset().
Logic:
- Create an empty table
- Create a metatable with
__newindexthat receives the table, key, and value - Inside
__newindex, check if the value is a string usingtype(value) == "string" - If it's a string, use
rawset(t, key, value)to store it - If it's not a string, use
rawset(t, key, "invalid")to store"invalid"instead - Attach the metatable to the table using
setmetatable() - Use
rawset()to add the initial key-value pair to the table (bypassing the metatable) - Return the table
Parameters:
key(string): The initial key to add to the tablevalue(string): The initial value to add to the table
Returns: A table with validation that only accepts string values, containing the initial key-value pair (table)
Cheat sheet
The __newindex metamethod is triggered when you attempt to assign a value to a key that doesn't exist in a table, allowing you to intercept and control modifications.
Basic example preventing new keys:
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- Output: Cannot add key: name
print(readOnly.name) -- Output: nilThe __newindex function receives three arguments: the table itself, the key being set, and the value being assigned.
Validating data with __newindex:
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- Output: Health must be a number!
player.health = 100 -- AllowedUse rawset(t, key, value) to bypass the metatable and directly set values in the table. This prevents infinite loops when setting values inside __newindex.
Try it yourself
function createValidatedTable(key, value)
-- Write code here
end
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Advanced Table Iteration
Iterating with pairs()Iterating with ipairs()pairs() vs. ipairs()Recap - Character Sheet4Introduction to Metatables
What is a Metatable?setmetatable & getmetatableThe __index MetamethodThe __newindex MetamethodThe __tostring MetamethodArithmetic Metamethods Part 1Arithmetic Metamethods Part 2Recap - Read-Only Table2More Table Library Functions
table.concat()table construction & unpack()table.sort()Custom Sorting with FunctionsRecap - High Score Board