About two and a half years ago, I started learning Go for the startup I had then joined which has long since failed (in spite of choosing Go). As I do when learning new programming languages, I eschewed all documentation and sashayed my way to greatness on the back of my previous, questionable programming experience. This led to a considerable amount of fun and exciting experimentation with coding practices along the way that has led me, finally, to the following declaration:

Stop trying to make (this *Thing) happen.

Do not use this as the name of the receiver in your method declaration. I do not do this anymore. I have, however, seen a number of people suggest doing this, and this is used in a considerable amount of golang on Github.

Stop doing this:

func (this *YourStruct) Method() {}

We did this for only a brief time at aforementioned failed startup, and even that brief encounter with this forever changed the way that I think about function receivers in method declarations. In fact, it made me forget about the differences between non-pointer and pointer receivers, which bit me in the ass today for a few minutes when a method I assumed was mutating the struct did not mutate the struct.

Pointers vs. Non-pointers

Before proceeding, it’s important to understand the not-really-at-all-subtle difference between pointer and non-pointer function receivers. People have written Good Words about the difference between pointer and non-pointer receivers:

Don’t Get Bitten by Pointer vs Non-pointer Method Receivers in Golang

Should methods be declared on T or *T

Even the Golang Tour’s section on pointer receivers mentions the difference between a pointer and non-pointer receiver, but this is a really crucial distinction that is unlikely to be easily recalled.

The inclination to think of Go as Object Oriented is strong. This is particularly the case when a developer’s first programming languages are all object-oriented — which is incredibly common. Their first encounter with method declarations with this as the receiver’s name, is likely to lead them to reason about the code as if it were object-oriented.

Go is not an object-oriented programming language.

They too will ignore the not-really-at-all-subtle difference between pointer and non-pointer function receivers in method declarations, because they too will assume that the receiver can always mutate the struct that received the method call even when the receiver is a non-pointer. They will do this, because that’s exactly what they expect when they see this in Python or Java, and now they have seen it in Go code. Method receivers are not an entirely intuitive concept. Continued use of this in Go will only propagate the inclination to improperly reason about Go as if it were object-oriented.

While it is becoming more common to hire experienced Go developers, my current team is composed of mostly engineers with little or no experience in Go. You’re still likely to hire Golang hobbyists than professionals with years of experience, and there’s a high probability that all of them (including some of the experienced ones) will operate under the assumption “when in doubt, use a pointer receiver.”

How did it come to this?

When we first started, we weren’t thinking of Go as object-oriented. The arrival at the conclusion to name pointer receivers this was so harmful that it actually changed the way that we thought about the programming language. We ended up using this because we got really tired of seeing this:

func (t Thing) Method() {}

The Go community has standardized around a single letter receiver name that is the first letter of the type’s name. Code that is lexically easier to parse by a human allows new developers to be more easily introduced to a codebase or an entirely new programming language. By using this in method declarations, you are actually harming your developers. You may as well stab them in the eyes with one of your many knives, because for all the feeling of “finally we know what to call all of our method receivers” you get to have, this is training new engineers to incorrectly reason about both the programming language and the behavior of the code they’re reading.

If Not This, Then…

What should we name method receivers?

My first idea was this:

package thing type Thing struct {} func (thing Thing) Method() {}

There are a couple of problems with this approach, however. First, the receiver’s name conflicts with the package name. This isn’t a problem for the Golang compiler, but it could potentially be confusing to readers. The more pressing problem, however, is that it doesn’t work if your type isn’t exported.

package thing type thing struct {} func (thing thing) Method() {}

Naming things: still the hardest problem in all of programming.

While that’s valid syntax, using the lower-cased type name as the receiver name disallows you from referencing the type from within your method’s body, because you’ve overriden the name. Trying to compile the following code will yield an error indicating that thing is not a type.

func (thing thing) DoubleField() thing {

newThing := thing{}

newThing.field = thing.field * 2

return newThing

}

That’s out of the question, then. Which leads us to my current Great Programming Thought:

type thing struct {} func (thingPtr *thing) Method() {} func (thingCpy thing) Method() {}

This is both lexically and semantically clear, and it’s valid syntax. My only qualm with this is that thingCpy could be misleading. This works well for most of the cases I can think of. The original struct isn’t mutable from within the method. Fields that are slices or primitives are copied. Any fields that are pointers point to the original addresses, but they’re pointers, so… working as intended?

If you can think of any reasons why thingPtr and thingCpy are terrible other than keystrokes, please share them in the comments… I guess. If your reason is “keystrokes,” well… Learn to use your editor.