Lua (__index) 存在しないキー参照時の動作

目次

概要

Luaの__indexは、テーブルに存在しないキーを参照したときの動作を定義するメタメソッドです。

通常、テーブルtに対してt[key]を実行した場合、以下になりますが

  • キーが存在する→その値を返す
  • キーが存在しない→nilを返す

メタテーブルに__indexを設定すると、キーが見つからなかった場合のフォールバック処理を定義できます。

 

主な用途

  • デフォルト値の提供
  • 継承(プロトタイプ)

__index に関数を設定した場合

基本の動き

local t = {}
print(t.x) -- nil

キーxが存在しないためnilが返ります。

 

__index に関数を設定した場合

local t = {}

local mt = {
    __index = function(tbl, key)
        return "'" .. key .. "' does not exist"
    end
}

setmetatable(t, mt)

print(t.a)  -- 'a' does not exist

11行目のt.aは存在しないキーです。

そのため__indexが呼ばれ、カスタムのエラーメッセージを返します。

__index にテーブルを設定する(デフォルト値 / 継承)

__index には関数だけでなく、テーブルも指定可能です。

local defaults = {
    a = "red",
    b = "green"
}

local t = {}

setmetatable(t, {
    __index = defaults
})

print(t.a) -- red
print(t.b) -- green

12,13行目のtに、aやbは存在しません。

9行目は、__index=defaultsのためdefaults.a,defaults.bが参照されます。

 

チェーン(連鎖)

__index 先のテーブルにもメタテーブルがある場合に再帰的に検索されます。

local t3 = { x = 100 }

local t2 = {}
setmetatable(t2, { __index = t3 })

local t1 = {}
setmetatable(t1, { __index = t2 })

print(t1.x)  -- 100

キーが見つからないので、t1→t2→t3と検索しました。

 

注意点:無限ループの可能性

setmetatable(t1, { __index = t2 })
setmetatable(t2, { __index = t1 })  -- 危険

循環参照になると無限に探し続けるので注意します。

キーが存在し、__indexにも同じキーがある場合

local t = { a = 100 }
local defaults = { a = "red", b = "green" }

setmetatable(t, { __index = defaults })

print(t.a)  -- 100(t側が優先)
print(t.b)  -- green(存在しないのでdefaults)

1行目、2行目ともにキーのaが存在します。
この場合、テーブル自身の値(tのキー)が優先されます。

rawgetとは(メタテーブルを無視して純粋な値を取得する)

rawgetは、テーブルからメタテーブルを無視して値を取得する関数です。

local t = {}

setmetatable(t, {
    __index = function()
        return "default"
    end
})

print(t.x) -- default
print(rawget(t, "x")) -- nil

9行目は、__indexが呼ばれています。
10行目は、__indexは呼ばれず、キーがないのでnilになります。

rawgetの利点

__index による見かけの値を排除できます。
デバッグで使えます。

関連の記事

Lua setmetatableでテーブルの振る舞いを変更する

△上に戻る