What exactly does self do?

I’m trying to learn OOP and self is used here in a way that I don’t understand.


Wouldn’t boost do nothing as there is nothing inside of the brackets for newcar:Boost()? As that is what self is automatically assigned as when using colons?

1 Like

using colons are fine

I don’t really know how to use self thought but try to read this
https://developer.roblox.com/en-us/articles/Method

Self just references the table you’re calling on:
image
In this case, self is the table newcar.

2 Likes

When you construct a new object, you are assigning its metatable __index to the Car table, which is where you are storing your methods (functions). Self is simply just a reference to the specific table (the object).

1 Like

Why is this tho? Please explain

What is the object in this case?

When they said:

local newcar = Car.new(Vector3.new(1, 0, 1), "Guest1892", game.ReplicatedStorage.F1Car)

newcar was the table returned from the module:

local newcar = Car.new(Vector3.new(1, 0, 1), "Guest1892", game.ReplicatedStorage.F1Car)
               ^
          return newcar

So when newcar:Boost() is called it’s referencing the newcar table.

1 Like

Does this have anything to do with the __index metamethod?

Using a colon is just syntactic sugar. If we have the following code:

local car = Car.new(Vector3.new(1, 0, 1), "Guest", game.ReplicatedStorage.Car)
car:Boost()

The car:Boost() line is actually equivalent to car.Boost(car). This may seem confusing however the function declaration also has the same syntax sugar.

Defining

function Car:Boost()
    self.Speed = self.Speed + 5
end

Is actually the same as writing

function Car.Boost(self)
    self.Speed = self.Speed + 5
end

So in this case we are actually calling Car.Boost(self) with car.Boost(car). This is how self magically appears and how modifying it modifies the car data. As this is such a common thing to do in Lua we can use colon as a short handed way of doing this.

2 Likes

Yes, because you’re setting the newtable metatable to the module (that has the __index method). __index runs if you call something that doesn’t exist in the original table, in which it will return something else. In this case, the rest of the module:

module.__index = module -- as you can see, this returns the module

Oh so self refers to the actual

Car = {}

itself?

Edit: sorry I’m just not getting it 100% as to how the metamethod is related to self and why it is set to car

Technically you don’t even need self.

self is just used because many people like to write code like the picture up above. It makes the code look more clean. It simply is a way to refer to the object itself.

When writing for OOP I like to think the code you write is a part of the object i. self is simply a keyword for accessing the object.

self is used cause it makes writing code way easier if u use colons in your new function its pretty much not available and u would have to put it in the brackets.

Here is an example that I gave yesterday:

local mt = {
	__index = {
		changeType = function(self, s)
			self.Species = s
		end;
		changeName = function(self, n)
			self.Name = n
		end
	}
}

local function createObject(name, species)
	-- self is just a table with an identity
	local self = {
		Name = name,
		Species = species
	}

	setmetatable(self, mt) 
	return self
end

local kermit = createObject("Kermit", "Frog") -- The actual object
local rizo = createObject("Rizo", "Mouse")

print(kermit)
print(rizo)
-- Oh no, Rizo isn't a mouse - he is a Rat!

rizo:changeType("Rat")

print(rizo)

The metamethod .__index is essential here, as it allows our objects to inherit the functions specified in the .__index table. Using Self is not even necessary, however it improves readability. You might find this of benefit.

No not quite. When we call Car.new() it makes a new table containing data about the car. This new table is called an object in OOP. When we then call methods on this object it is passed to those methods. Perhaps this snippet might clarify a few things.

local Class = {}

-- These two methods are equivalent
function Class.method1(self)
	print(self.value)
end

function Class:method2()
	print(self.value)
end

-- Let's make some objects
local a = { value = "I'm a!" }
local b = { value = "I'm b!" }

-- Tell Lua to look into Class for the methods
setmetatable(a, { __index = Class })
setmetatable(b, { __index = Class })

-- These are all equivalent too
a.method1(a)
a:method1() -- This is valid even though we define it without the colon

a:method2()
a.method2(a) -- Similar here execept other way around

-- Using b will provide b's data instead
b:method2()
b.method2(b)

-- We can also use the Class methods "statically"
Class.method1(a)
Class.method1({ value = "Who am I?"})
-- We cannot use a colon though as Class doesn't contain our data
Class:method2() -- We cannot provide an object here

Also to clarify, the metamethod stuff is only done so we don’t have to do everything statically. Instead of doing Class.method(object, a, b, ...) the metamethod stuff lets us do object:method(a, b, ...) instead. This is the same but just easier to read.

2 Likes

In this case:


because the :Boost() is being called on newcar, self is actually equal to newcar in this case? But however the function :Boost() still confuses me as
image
there is no ‘Speed’ property in both Car = {} or newcar.

Yep nice catch, if you actually tried to run the code it will error (Have you tried it out?)

If so then just add speed to the property.

function Car.new(position, driver, model)
    local newcar = {}
    setmetatable(newcar, Car)

    newcar.Position = position
    newcar.Driver = driver
    newcar.Model = model
    newcar.Speed = 0

    return newcar
end

Then if you boost a newcar object it’ll go from 0 speed to 5 speed.

1 Like

(I know this would be messy, just for the sake of understandment) theoretically if the boost property was inside Car = {} because newcar’s metatable is Car = {} would it still work?

You should try it out yourself. These are the questions you ask the lua compiler and whatever the lua compiler says is Law.

Car = {
    Speed = 0
}
Car.__index = Car

function Car.new(position, driver, model)
    local newcar = {}
    setmetatable(newcar, Car)

    newcar.Position = position
    newcar.Driver = driver
    newcar.Model = model
newcar.Speed = 0
    return newcar
end

function Car:Boost()
    print(self.Speed)
    self.Speed = self.Speed + 5
    print(self.Speed)
end

Car:Boost()
local newCar = Car.new()
newCar:Boost()

The output will be
The speed in the Car table goes 0 to 5
The speed in the newcar object created goes 0 to 5

I believe you are confused about the metamethod.

local newCar = Car.new()
--newCar is a property table only containing speed only
--rest is nill cause nothing in parameters ()


newCar:Boost() -- this has a lot of steps
--newCar does not have the boost function
--It borrows from the Car table because metamethod .__index stuff
--Now the newCar table contains the :Boost() function
--This boost function locates the speed property in the current table which is newcar
--increases it from 0 to 5
1 Like