This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


13.4.1 – The __index Metamethod

I said earlier that, when we access an absent field in a table, the result is nil. This is true, but it is not the whole truth. Actually, such access triggers the interpreter to look for an __index metamethod: If there is no such method, as usually happens, then the access results in nil; otherwise, the metamethod will provide the result.

The archetypal example here is inheritance. Suppose we want to create several tables describing windows. Each table must describe several window parameters, such as position, size, color scheme, and the like. All these parameters have default values and so we want to build window objects giving only the non-default parameters. A first alternative is to provide a constructor that fills in the absent fields. A second alternative is to arrange for the new windows to inherit any absent field from a prototype window. First, we declare the prototype and a constructor function, which creates new windows sharing a metatable:

    -- create a namespace
    Window = {}
    -- create the prototype with default values
    Window.prototype = {x=0, y=0, width=100, height=100, }
    -- create a metatable
    Window.mt = {}
    -- declare the constructor function
    function Window.new (o)
      setmetatable(o, Window.mt)
      return o
    end
Now, we define the __index metamethod:
    Window.mt.__index = function (table, key)
      return Window.prototype[key]
    end
After that code, we create a new window and query it for an absent field:
    w = Window.new{x=10, y=20}
    print(w.width)    --> 100
When Lua detects that w does not have the requested field, but has a metatable with an __index field, Lua calls this __index metamethod, with arguments w (the table) and "width" (the absent key). The metamethod then indexes the prototype with the given key and returns the result.

The use of the __index metamethod for inheritance is so common that Lua provides a shortcut. Despite the name, the __index metamethod does not need to be a function: It can be a table, instead. When it is a function, Lua calls it with the table and the absent key as its arguments. When it is a table, Lua redoes the access in that table. Therefore, in our previous example, we could declare __index simply as

    Window.mt.__index = Window.prototype
Now, when Lua looks for the metatable's __index field, it finds the value of Window.prototype, which is a table. Consequently, Lua repeats the access in this table, that is, it executes the equivalent of
    Window.prototype["width"]
which gives the desired result.

The use of a table as an __index metamethod provides a cheap and simple way of implementing single inheritance. A function, although more expensive, provides more flexibility: We can implement multiple inheritance, caching, and several other variations. We will discuss those forms of inheritance in Chapter 16.

When we want to access a table without invoking its __index metamethod, we use the rawget function. The call rawget(t,i) does a raw access to table t. Doing a raw access will not speed up your code (the overhead of a function call kills any gain you could have), but sometimes you need it, as we will see later.