.__index and setmetatable

I don’t understand how .__index and setmetatable work and how to create classes and multiple inheritance with them. Can anyone explain to me this or at least tell where I can find out more about it(because I couldn’t find any information about it)?

2 Likes

How .__index and setmetatable work

Lua has only one core data structure: table.

metatable is actually a table with “__index” field. And “__index” field can be a table or a function.

If a table has metatable, then when a field is not found in the table, it will attempt to look it up through the metatable’s __index field.

-- Create a "prototype" table with some default values
local prototype = {x = 0, y = 0, width = 100, height = 100}

-- Create an empty metatable
local mt = {}
-- Set the __index field of the metatable to point to our prototype table
mt.__index = prototype

-- Create a new instance table
local w = {x = 10, y = 20}
-- Set the metatable for the instance table
setmetatable(w, mt)

-- Now, accessing a field that does not exist in w will look it up in prototype
print(w.width)  -- Output: 100
local mt = {
    __index = function(table, key)
        return "You accessed a field that does not exist: " .. key
    end
}

local t = {}
setmetatable(t, mt)

print(t.hello)  -- Output: You accessed a field that does not exist: hello

How to create class

-- 1. Define the class (a table)
Person = {}
-- 2. Set __index to point to itself (this is the essence)
Person.__index = Person

-- 3. Define the constructor (usually named new)
function Person:new(name, age)
    local obj = {}          -- Create an empty instance
    setmetatable(obj, self) -- Set Person as the metatable of obj
    -- Initialize instance properties
    obj.name = name
    obj.age = age
    return obj
end

-- 4. Define class methods (note the use of colon syntax to ensure self is passed)
function Person:introduce()
    print("My name is " .. self.name .. ", and I am " .. self.age .. " years old.")
end

-- Create instances using the class
local p1 = Person:new("Li Si", 25)
local p2 = Person:new("Wang Wu", 30)

p1:introduce() -- Output: My name is Li Si, and I am 25 years old.
p2:introduce() -- Output: My name is Wang Wu, and I am 30 years old.

Search Process Analysis:

When p1:introduce() is executed:

Lua ​​searches for introduce in the p1 table, but finds no match.

It then discovers that p1 has a metatable Person, and Person.__index points to Person.

Therefore, it searches for introduce in the Person table, finds it, and calls it.

Why must the Person table have an __index field?

Because if Person doesn’t have an __index field, then Person isn’t a metatable, and the instance cannot set Person as a metatable. .Therefore, the instance cannot find and use the methods in Person.

How to create single inheritance

single inheritance allow subclasses to reuse parent class code.

To achieve inheritance, the approach remains the same: using a metatable.

Give each subclass its own metatable.

Set the __index of this metatable to point to the parent class.

Meanwhile, the subclass’s own __index still points to itself (so instances can find the subclass’s methods).

-- Parent class: Animal
Animal = {}
Animal.__index = Animal

function Animal:new(name)
    local obj = {}
    setmetatable(obj, self)
    obj.name = name
    return obj
end

function Animal:speak()
    print(self.name .. " made a sound.")
end

-- Subclass: Dog
Dog = {}
Dog.__index = Dog

-- Set Dog's metatable so it can find Animal's methods
setmetatable(Dog, { __index = Animal })

-- Override constructor (call parent constructor)
function Dog:new(name, breed)
    local obj = Animal:new(name) -- First create a parent instance
    setmetatable(obj, self)      -- Change the metatable to Dog (so that Dog's methods are found)
    obj.breed = breed
    return obj
end

-- Override method
function Dog:speak()
    print(self.name .. " barks.")
end

-- New method
function Dog:fetch()
    print(self.name .. " fetched the ball.")
end

-- Usage
local d = Dog:new("Wangcai", "Golden Retriever")
d:speak()  -- Output: Wangcai barks. (calls Dog's method)
d:fetch()  -- Output: Wangcai fetched the ball. (calls Dog's method)
3 Likes

Lua doesn’t have classes nor inheritance mechanism. Instead, you have metatables, that you can override. All tables in fact contain metatables, not visible to users. What people call “classes” in Lua are usually just tables and metatables with properly defined behaviour. Because metatables changes how the object behaves, when you use Lua specific syntax and operators, e.g. __index can be, simply put, a function that is called when you get some field from the table, e.g. my_table[1].

Here you can find a Lua classes implementation without metatables:

The best place to start learning Lua is its official documentation, it’s pretty short, and nicely written:

3 Likes

Buy: Programming in Lua, by Roberto Lerusalimschy.
A real paper copy of this book to read in slow time is ideal.
It is the horses mouth, everything else is a reflection.

3 Likes

thank you guys. It was helpful. Now it’s more clear for me

2 Likes