What is the difference between a Go receiver (as in “method receiver”) and a function argument? Consider these two bits of code:
1 2 3 |
|
versus
1 2 3 |
|
The “do something” part above would work exactly the same regardless of how you declare the function. Which begs the question, which should you use?
In the object-oriented world we were used to objects doing things, and
in that context d.quack()
may seem more intuitive or familiar than
quack(d)
because it “reads better”. After all, one could argue that
the former is a duck quacking, but the latter reads like you’re
quacking a duck, and what does that even mean? I have learned that you
should not think this way in the Go universe, and here is why.
First, what is the essential difference? It is that at the time of the call, the receiver is an interface and the function to be called is determined dynamically. If you are not using interfaces, then this doesn’t matter whatsoever and the only benefit you are getting from using a method is syntactic sweetness.
But what if you need to write a test where you want to stub out
quack()
. If your code looks like this, then it is not possible,
because methods are attached to their types inflexibly, you cannot
change them, and there is no such thing as a “method variable”:
1 2 3 4 5 6 7 8 9 10 |
|
However, if you used a function argument, it would be easy:
1 2 3 4 5 6 7 8 9 10 |
|
Now you can assign another function to quack at test time, e.g. quack = func(d *duck) { // do something else }
and all is
well.
Alternatively, you can use an interface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Here, if we need to test foo()
we can provide a different
quacker
.
Bottom line is that it only makes sense to use a receiver if this
function is part of an interface implementation, OR if you never ever
need to augment (stub) that function for testing or some other
reason. As a practical matter, it seems like (contrary to how it’s
done in the OO world) it is better to always start out with quack(d)
rather than d.quack()
.