26. Methods and self
You have called functions stored on tables many times —
string.format, table.insert,
math.floor. This chapter shows how to define your own
functions that belong to a particular table, and what the colon
syntax (obj:method()) does under the hood. The colon is
everywhere in Roblox; understanding it once explains a lot of Roblox
code.
Functions on tables, the long way
A function on a table is just a value at a string key:
local counter = { count = 0 }
function counter.step(c)
c.count = c.count + 1
end
counter.step(counter)
counter.step(counter)
counter.step(counter)
print(counter.count) -- 3The function counter.step takes the table itself as its
first argument, then uses it to update the counter. Every call has to
pass counter explicitly:
counter.step(counter). This works but reads awkwardly.
The colon shortcut
Lua's colon does two things at once — a bit of syntax sugar that makes the table-bound calling style cleaner.
Calling with : automatically passes the
table as the first argument:
counter:step() -- exactly the same as counter.step(counter)Defining with : automatically declares
a parameter named self that receives the table:
function counter:step()
self.count = self.count + 1
endSame definition as before, written more compactly. Combine both shortcuts:
local counter = { count = 0 }
function counter:step()
self.count = self.count + 1
end
counter:step()
counter:step()
counter:step()
print(counter.count) -- 3Inside the body, self is just a normal parameter name.
Write self.x to read a field, self.x = ... to
change one. The name self is a convention, not a rule — it
is just the implicit parameter the colon definition creates.
Open exercises/26/01-counter.lua. It already has
counter:step. Add a counter:reset that sets
self.count back to 0, and call it from the
bottom of the file.
Built-in colon calls
Many built-in functions accept either dot or colon syntax. The two forms are interchangeable:
local name = "keiko"
print(string.upper(name)) -- KEIKO
print(name:upper()) -- KEIKO -- samename:upper() passes name as the first
argument to string.upper. This works because strings carry
an implicit link to the string table, so any string method
can be called as s:method(). You will see this everywhere
in real Lua code.
A small game object
Method syntax shines when several pieces of state go together. Here is a dog with a name and a number of barks left:
local dog = {
name = "Rex",
barks_left = 3,
}
function dog:bark()
if self.barks_left <= 0 then
print(self.name .. " is hoarse.")
return
end
print(self.name .. ": Woof!")
self.barks_left = self.barks_left - 1
end
dog:bark() -- Rex: Woof!
dog:bark() -- Rex: Woof!
dog:bark() -- Rex: Woof!
dog:bark() -- Rex is hoarse.The dog table holds the state; the method changes it on
every call. This is the bit of object orientation you need for the next
chapter, and the same pattern you see in Roblox: an
Instance (a table) with methods like
:Destroy(), :GetChildren(),
:Clone().
Common mistake: mixing the two forms
obj.method() and obj:method() are
not the same call. The dot does not pass
obj automatically. If the method was defined with
:, calling with . and forgetting to pass
obj leaves self as nil:
dog.bark() -- error: attempt to index a nil value (local 'self')The fix is one character: use :.
The reverse mistake — obj:method(obj) — passes
obj twice. The first goes into self, the
second becomes an extra argument. Usually it is just ignored, but it can
confuse debugging.
Homework
Problem 1 — Counter object
Open exercises/26/homework/01-counter-object.lua. Build
a counter table with the fields and methods:
counter.count— starts at0.counter:inc()— adds1.counter:get()— returns the current count.counter:reset()— setscountback to0.
Call them in sequence and print the value at each step.
Problem 2 — Dog with bark
Open exercises/26/homework/02-dog.lua. Build a
dog table with a name field and a
:bark() method that prints
<name>: Woof!. Make a second dog with a different
name. Call both :bark() and confirm each prints its own
name.
Problem 3 — Calculator object
Open exercises/26/homework/03-calculator.lua. Build a
calc table with:
calc.value— starts at0.calc:add(n)— addsntocalc.value.calc:sub(n)— subtracts.calc:mul(n)— multiplies.calc:show()— prints the current value with a label.
Chain a few calls and confirm the value updates as expected.
Challenge — Stack
Open exercises/26/homework/04-stack.lua. Build a
stack table that behaves like a stack:
stack.items— a list.stack:push(v)— addsvto the top.stack:pop()— removes and returns the top.stack:peek()— returns the top without removing it (ornilif empty).stack:size()— returns how many items are stored.
Use table.insert and table.remove without
the second argument — that pushes and pops at the end of the list, the
standard stack behaviour.
Stuck or finished? Open the homework solutions page.