When you want non-mutating methods in Go
In Should methods be declared on T or *T, Dave Cheney included a footnote:
5. If the method does not mutate its receiver, does it need to be a method?
(One alternative Dave Cheney had in mind was a pure function that's passed the necessary fields directly, cf. I also suspect that Dave Cheney put this in more to make readers think about the issue than as an outright directive.)
I'll bite on that somewhat rhetorical question, because I see several possible reasons to want a method that merely reads things from the receiver without modifying it.
The biggest one is interfaces. Interfaces can only contain methods,
so if you want an interface to give you some form of polymorphism
over fields you need either getter methods or a method that does
the actual field based computation. We can see this all over the
Go standard library; for instance net.Conn contains some functions that are
more or less getters, namely LocalAddr()
and RemoteAddr()
.
Even without interfaces in the picture, encapsulating fields inside method functions preserves your future freedom of action. Maybe the computation involving fields is simple today and easily done directly by outside people, or maybe today the getter functions are trivial. But tomorrow the calculations could be complex or the field values generated through non-trivial calculations; in both cases, you'd want to change the API away from direct field access. Obviously this is less compelling when it involves fields that aren't exported outside your package, because then they aren't part of any public API. So there I'm with Dave Cheney, because this is at least something to think about.
Finally, you may reasonably feel that having read-only method
functions simply makes the (internal) API simpler and more regular.
There's definitely a consistency value in always doing x.thing()
instead of sometimes doing x.writething()
and other times doing
readthing(x.field1, x.field2)
. And if you're tempted to write
just readthing(x)
I would definitely make it a method, because
x.readthing()
and readthing(x)
are almost directly equivalent.
|
|