Difference between . and : in Lua
Asked Answered
L

3

248

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?

Legitimize answered 6/2, 2011 at 2:43 Comment(1)
Related: #3780171Enquire
G
352

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).

Gorki answered 6/2, 2011 at 2:55 Comment(10)
ah... so it's object-oriented syntactic sugar.Legitimize
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)Gorki
ooh ahh... I was going to ask where the official docs were on this, but you beat me to it. nicely done. :-)Legitimize
@keyle It depends on the self object will go as the first parameter and its properties value.Misunderstanding
@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 :.Inefficacious
Now why would you ever want to use this? Doesn't seem like it's useful for this particular case.Spearhead
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?Charter
So what happens if you pass another object as self? x.bar(y,3,4) will this result in an error?Perilune
@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.Amberlyamberoid
@Charter 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.Amberlyamberoid
W
49

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).

Waldrop answered 4/10, 2013 at 15:58 Comment(0)
T
36

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).

Triacid answered 17/8, 2018 at 11:32 Comment(2)
You really shouldn't worry about such stuff. If you have to, there is something just terribly wrong with your architecture.Crossover
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.Triacid

© 2022 - 2024 — McMap. All rights reserved.