Menu
CoddyTech

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: nil

When 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 now

Notice 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: 100

player never gets a key of its own, so every assignment goes through __newindex, and every read falls through __index to data.

challenge icon

Challenge

Easy

Write 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 __newindex that receives the table, key, and value
  • Inside __newindex, check if the value is a string using type(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 table
  • value (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
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Logic & Flow

Practice on your own: Online Lua compiler