Menu
CoddyTech

La metaméthode __newindex

Fait partie de la section Logique et déroulement du programme du Journey Lua de Coddy. Leçon 19 sur 54.

Tu as appris comment __index contrôle ce qui se passe lorsque tu essaies de lire une clé qui n’existe pas dans une table. Tu vas maintenant découvrir son pendant : __newindex, qui contrôle ce qui se passe lorsque tu affectes une valeur à une clé que la table ne possède pas encore.

La métaméthode __newindex est déclenchée chaque fois que tu essaies d’assigner une valeur à une clé qui n’existe pas actuellement dans la table. Cela te permet d’intercepter et de contrôler les modifications avant qu’elles ne se produisent.

Voici un exemple simple qui empêche l’ajout de nouvelles clés à une table :

local readOnly = {}
local meta = {
    __newindex = function(table, key, value)
        print("Cannot add key: " .. key)
    end
}

setmetatable(readOnly, meta)

readOnly.name = "Alice"  -- Sortie : Cannot add key: name
print(readOnly.name)     -- Sortie : nil

Lorsque tu essaies de définir readOnly.name = "Alice", Lua constate que name n’existe pas dans la table. Au lieu de l’ajouter, Lua appelle la fonction __newindex avec trois arguments : la table elle-même, la clé à définir et la valeur attribuée.

Dans cet exemple, la fonction affiche simplement un message et ne stocke rien en réalité.

Tu peux aussi utiliser __newindex pour valider les données avant de les laisser entrer dans ta 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"  -- Sortie : Health must be a number!
player.health = 100     -- Autorisé, stocké avec rawset
player.health = "high"  -- Aucun message : health existe maintenant

Remarquez l’utilisation de rawset() dans le deuxième exemple. Cette fonction contourne la métatable et définit directement la valeur dans la table.

Sans cela, définir la valeur déclencherait à nouveau __newindex, créant une boucle infinie.

Comme __newindex ne s’exécute que pour les clés que la table ne possède pas, cette vérification protège la première affectation de chaque clé. Une fois que rawset a stocké health, les affectations suivantes la modifient directement. Pour vérifier chaque affectation, laisse la table elle-même vide et stocke les valeurs dans une seconde table, à laquelle elle accède par __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"  -- Sortie : Health must be a number!
print(player.health)    -- Sortie : 100

player n’obtient jamais de clé qui lui soit propre : chaque affectation passe donc par __newindex, et chaque lecture passe par __index pour accéder à data.

challenge icon

Défi

Facile

Écris une fonction createValidatedTable qui prend key et value et renvoie une table qui n’accepte que les valeurs de type chaîne pour les nouvelles clés.

Crée une table vide et associe-lui une métatable avec __newindex qui valide le type de la valeur. Si quelqu’un essaie d’ajouter une valeur qui n’est pas une chaîne, utilise rawset() pour stocker la chaîne "invalid" à la place. Pour les valeurs de type chaîne, stocke-les normalement avec rawset().

Logique :

  • Crée une table vide
  • Crée une métatable avec __newindex qui reçoit la table, la clé et la valeur
  • Dans __newindex, vérifie si la valeur est une chaîne à l’aide de type(value) == "string"
  • S’il s’agit d’une chaîne, utilise rawset(t, key, value) pour la stocker
  • S’il ne s’agit pas d’une chaîne, utilise rawset(t, key, "invalid") pour stocker "invalid" à la place
  • Associe la métatable à la table à l’aide de setmetatable()
  • Utilise rawset() pour ajouter la paire clé-valeur initiale à la table (en contournant la métatable)
  • Renvoie la table

Paramètres :

  • key (chaîne) : la clé initiale à ajouter à la table
  • value (chaîne) : la valeur initiale à ajouter à la table

Valeur de retour : une table avec une validation qui n’accepte que les valeurs de type chaîne et qui contient la paire clé-valeur initiale (table)

Essayez vous-même

function createValidatedTable(key, value)
    -- Écrivez le code ici
end
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Logique et déroulement du programme

Entraînez-vous par vous-même : Compilateur Lua en ligne