244

I am confused about the difference between function calls via . and via :

local x = {
    foo = function(a, b) return a end,
    bar = function(a,b) return b end
}

return x.foo(3, 4) -- 3
return x.bar(3, 4) -- 4
return x:foo(3, 4) -- table: 0x10a120
return x:bar(3, 4) -- 3

What is the : doing?

Skully
  • 2,882
  • 3
  • 20
  • 31
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 1
    Related: http://stackoverflow.com/questions/3779671/why-cant-i-use-setunion-instead-of-set-union – finnw Feb 06 '11 at 11:38

3 Answers3

342

The colon is for implementing methods that pass self as the first parameter. So x:bar(3,4)should be the same as x.bar(x,3,4).

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
BMitch
  • 231,797
  • 42
  • 475
  • 450
  • 90
    ah... so it's object-oriented syntactic sugar. – Jason S Feb 06 '11 at 03:02
  • 15
    Exactly. In the entire reference manual, the only blurb they give on this is "The colon syntax is used for defining methods, that is, functions that have an implicit extra parameter self." (5.0 manual, bottom of pdf page 19) – BMitch Feb 06 '11 at 03:24
  • 4
    ooh ahh... I was going to ask where the official docs were on this, but you beat me to it. nicely done. :-) – Jason S Feb 06 '11 at 15:09
  • 1
    @keyle It depends on the `self` object will go as the first parameter and its properties value. –  Feb 09 '16 at 13:16
  • 12
    @keyle Colon syntax would be a little faster if the object you're calling is not a local, since the virtual machine retrieves it only once. Basically dot syntax like `object.method(object,args)` retrieves `object` twice, while `object:method(arg)` retrieves `object` only once. If `object` is a global, upvalue or table field, then `:` is faster than `.`. `.` is never faster than `:`. – negamartin Nov 05 '16 at 08:08
  • Now why would you ever want to use this? Doesn't seem like it's useful for this particular case. – Unknow0059 Jan 17 '21 at 03:50
  • I've just found this type of code in a plugin I'm trying to understand. I wanted to check something, if the function should only have 2 parameters like `function bar(a, b)`. If you call it with `x:bar(3,4)`, which is like `x.bar(x,3,4)` does the 'extra' 3rd parameter get ignored? So you'd end up with `a = x` (self) and `b = 3`? – TC0072 Sep 19 '21 at 00:11
  • So what happens if you pass another object as self? `x.bar(y,3,4)` will this result in an error? – N. Janné Mar 24 '22 at 14:04
  • @N.Janné it is valid to do that and, as long as y also implements the methods of x you use inside the function, a priori you should see no error. – Julian Martin Del Fiore Jan 15 '23 at 08:21
  • @TC0072 yes that's the case, but note that the function should be callable inside x, e.g. having defined function x.bar(a, b). Then, if you call it as you were suggesting, if you use a friendly IDE, most likely you will see a warning that will help you notice that you are passing more parameters than needed. – Julian Martin Del Fiore Jan 15 '23 at 08:29
46

For definition it is exactly the same as specifying self manually - it will even produce same bytecode on compilation. I.e. function object:method(arg1, arg2) is same as function object.method(self, arg1, arg2).

On use : is almost the same as . - a special kind of call will be used internally to make sure object and any possible side-effects of calculations/access are calculated only once. Calling object:method(arg1, arg2) is otherwise same as object.method(object, arg1, arg2).

Oleg V. Volkov
  • 21,719
  • 4
  • 44
  • 68
35

To be completely precise, obj:method(1, 2, 3) is the same as

do
  local _obj = obj
  _obj.method(_obj, 1, 2, 3)
end

Why the local variable? Because, as many have pointed out, obj:method() only indexes _ENV once to get obj. This normally just important when considering speed, but consider this situation:

local tab do
  local obj_local = { method = function(self, n) print n end }
  tab = setmetatable({}, {__index = function(idx)
    print "Accessing "..idx
    if idx=="obj" then return obj_local end
  end})
end
tab.obj.method(tab.obj, 20)
--> Accessing obj
--> Accessing obj
--> 20
tab.obj:method(10)
--> Accessing obj
--> 10

Now imagine the __index metamethod did more than just printing something. Imagine it increased a counter, logged something to a file or deleted a random user from your database. There's a big difference between doing that twice or only once. In this case, there's a clear difference between obj.method(obj, etc) and obj:method(etc).

DarkWiiPlayer
  • 6,871
  • 3
  • 23
  • 38
  • 2
    You really shouldn't worry about such stuff. If you have to, there is something just **terribly** wrong with your architecture. – val - disappointed in SE Jan 16 '19 at 11:31
  • 8
    I'd say it's the other way around; good code should not make any assumptions about implementation details of unrelated code. Function calls may or may not be memoized, that doesn't mean it's good practice to call them more often than needed. – DarkWiiPlayer Jan 16 '19 at 17:49