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 assign to a key that the table doesn't have yet.
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 -- Allowed, stored with rawset
player.health = "high" -- No message: health exists nowNotice 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.
Because __newindex only runs for keys the table doesn't have, this check guards the first assignment of each key. Once rawset has stored health, later assignments change it directly. To check every assignment, keep the table itself empty and store the values in a second table that it reads through __index:
local data = {}
local player = setmetatable({}, {
__index = data,
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
data[key] = value
end
end
})
player.health = 100
player.health = "high" -- Output: Health must be a number!
print(player.health) -- Output: 100player never gets a key of its own, so every assignment goes through __newindex, and every read falls through __index to data.
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)
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 BoardPractice on your own: Online Lua compiler