OPP Error - attempt to index nil with '__index'

I’ve learned about OOP (object-oriented-programming), but have never used, I wanted just to try it out, and so I did: (Code has been updated)

Person = {gender = "Male", age = 30, weight = 140}

function Person.New(self,o)

self.__index = self

setmetatable(o or {},self)

return o

end

function Person:SetGender(setGender)

if setGender == "Male" or "Female" then

print("Gender Set")

self.gender = setGender

else

error("Gender invalid")

end

end

function Person:SetAge(setAge)

print("Age Set")

self.age = setAge

end

function Person:SetWeight(setWeight)

print("Weight Set")

self.Weight = setWeight

end

MyPerson = Person.New()

MyPerson:SetGender("Female")

wait(2)

MyPerson:SetAge(44)

wait(2)

MyPerson:SetWeight(160)

wait(2)

MyPerson:SetGender("Fasdsae")

My goal is for it to print “Gender Set”, “Age Set”, “Weight Set”, and finally error “Gender invalid” . However when I run this I get an error that says:
attempt to index nil with '__index'

Hey, try doing

Person = {gender = "Male", age = 30, weight = 140}
Person.__index = Person

function Person.New(o)

local self = setmetatable(o or {}, Person)

return self

end

MyPerson = Person.New()

Also the methods should be methods of the class, not the object, so instead of

function MyPerson:SetAge(setAge)

You’d do

function Person:SetAge(setAge)
1 Like

That’s because self in your constructor is nil. Note the error stating that you’re attempting to access the __index of nil (self here is nil) which is invalid. Your constructor has two arguments and you use this constructor without passing any arguments, therefore you’ve got nil from your arguments.

With OOP, you typically never write self in any function parameters. Self is what you use when you’re accessing members of the class (or object) through a method defined with a colon because in that case self is implicitly declared as the table which you’re calling a method on.

local foobar = {}

function foobar:garply()
    -- self refers to the table foobar
end

function foobar.frobnicate()
    -- self does not exist in this function's environment
end

When it comes to OOP in Lua, if self is in a constructor function then it’s just referring to the object that’ll be returned but it does not have any implicit declarations like methods do.

-- Anything in the Person table is class-level, so in your case you have
-- a male profile as class-level properties. If you wanted defaults,
-- those should be explicitly defined, use case permitting. You otherwise
-- do not need to fill this table and can just instantiate a blank profile.
local Person = {Gender = "Male", Age = 30, Weight = 140}
Person.__index = Person

function Person.new()
    return setmetatable({}, Person)
end
2 Likes

Good explanation, my reply probably lacked that. Also just realised there was no need to define a self variable in the constructor as it doesn’t get used anyway.

According to the official Lua programming book, it says that the colon is syntactic sugar. And self is used as a parameter for OOP methods declared with a period, not a colon. I tried deleting the self parameter in `Person.new’, put everything in the function becomes underlined, indicating that the code is invalid.

Thank you I tried that, here is my final code:

Person = {gender = "Male", age = 30, weight = 140}

Person.__index = Person

function Person.New(o)

local self = setmetatable(o or {},Person)

return self

end

function Person:SetGender(setGender)

if setGender == "Male" or setGender == "Female" then

print("Gender Set "..self.gender)

self.gender = setGender

else

error("Gender invalid")

end

end

function Person:SetAge(setAge)

print("Age Set")

self.age = setAge

end

function Person:SetWeight(setWeight)

print("Weight Set")

self.Weight = setWeight

end

MyPerson = Person.New()

MyPerson:SetGender("Female")

wait(2)

MyPerson:SetAge(44)

wait(2)

MyPerson:SetWeight(160)

wait(2)

MyPerson:SetGender("Fasdsae")

The colon is syntactic sugar for defining self. The alternate method is passing the object you’re calling the method on itself. This is not really ideal for OOP-in-Lua though because you want to have self in order to mutate object state rather than class state and it generally looks (and is) messier.

local foobar = {}

function foobar:frobnicate() end

-- The above can be called as either:

foobar:frobnicate()
foobar.frobnicate(foobar)

This is primarily what said syntactic sugar is referring to. If you’re using self as a parameter in a function declared with a period then you need to call the method with a colon.

local foobar = {}

function foobar.frobnicate(self, ...) end

foobar:frobnicate("hello")

Constructor functions never implement self unless using it as the variable for the created object, so your example in itself is not correct. The reason why your code underlines and is red is because you removed the parameter but did not declare a self variable in the constructor scope, so naturally if there’s no self to use then how are you going to interact with self? Removing self from the parameters isn’t sufficient enough. You need to read into errors as well as the offered advice a little more carefully. These are problems you could’ve caught earlier.

Self is not necessary in your constructor example at all, unless you’re unconventionally writing the constructor to be called with a colon and not a period, which then yes having self as a parameter would be valid but then you need to likewise call it that way as Person:New(). Since you set it up as Person.New(self, o) and called it as Person.new(), both arguments are nil. If you called it as Person:New(), self would at least be non-nil.

EDIT: If it were up to me to write this class, or provide an example of my advice in action, here’s some basic boilerplate for OOP in Lua:

local Person = {}
Person.__index = Person

--- Construct a blank person containing Gender, Age and Weight data
function Person.new()
	return setmetatable({
		Gender = "Unspecified",
		Age = 0,
		Weight = 0
	}, Person)
end

function Person:SetGender(gender)
	self.Gender = gender
end

function Person:SetAge(age)
	self.Age = age
end

function Person:SetWeight(weight)
	self.Weight = weight
end

function Person:OutputProfile()
	print(self)
end

local MyPerson = Person.new()
MyPerson:OutputProfile()
MyPerson:SetGender("Female")
MyPerson:OutputProfile()
MyPerson:SetAge(44)
MyPerson:SetWeight(160)
MyPerson:OutputProfile()

Notice that nowhere do I use self except from within methods declared with colons because it’s otherwise not necessary to do so. In the instance that I instead declared those methods with dot syntax then I would need to include self as a parameter and then call it accordingly with a colon or pass the calling object.

-- Add this before constructing MyPerson
function Person.DotDeclaredOutputProfile(self)
    print(self)
end

-- Add this after constructing MyPerson
MyPerson:DotDeclaredOutputProfile()
MyPerson.DotDeclaredOutputProfile(MyPerson)

Just a simple matter of fixing your constructor and reviewing errors in my opinion.