skip to content

In Go, what should a method's receiver be named, and why is this or self discouraged?

level: juniorimportance: should knowfreq 55%

answer

  1. short scope, short name
  2. usually the type's first letter
  3. the same one in all thirty methods
  4. Go binds no hidden keyword

basics

~20 s

Use one or two lowercase letters derived from the type — b for Buffer, v for Vec2 — and the same name in every method of that type. Go has no receiver keyword: the receiver is an ordinary parameter you name yourself, so this and self read as imports from another language.

solid answer

~50 s

The receiver is just a parameter with a name and a type, so Go names it the way it names any short-lived variable: one or two letters, usually the first letter of the type, lowercase. `bytes.Buffer` methods use `b`, `time.Time` methods use `t`. The strong convention is that **every** method on a type uses the **same** receiver name — a reader who has seen three methods knows what `p` means in the fourth, and code can be moved between methods without renaming. Spelling out the type (`func (customer *Customer)`) is noisy and can shadow a package or variable of that name. `this` and `self` are not keywords in Go, so they compile, but they suggest class semantics — implicit binding, inheritance, constructors — that Go does not have. Nothing in the toolchain enforces any of this; reviewers do.

code

go · 7 lines
go
type Vec2 struct{ X, Y float64 }

func (v Vec2) Length() float64 { return math.Hypot(v.X, v.Y) }

func (v Vec2) Add(w Vec2) Vec2 { return Vec2{v.X + w.X, v.Y + w.Y} }

func (v Vec2) Scaled(f float64) Vec2 { return Vec2{v.X * f, v.Y * f} }

go deeper

for a junior

Be ready to state the rule in one breath: one or two lowercase letters taken from the type, the same name in every method of that type, and never this or self. Show it on a small type.

for a middle

Explain why the rule exists: the receiver is a plain parameter in a very short scope, a shared name lets code move between methods unchanged, and a short abbreviation avoids shadowing an imported package inside the body.

for a senior

Show how you hold the convention as a type grows past twenty or thirty methods, since no tool checks it. Talk about catching drift in review and about keeping the type's methods together so the existing name is obvious.

for a principal

Frame receiver naming as part of a package's readability contract that costs nothing to keep and is expensive to retrofit. Be ready to say why you would not spend CI budget enforcing it mechanically.

## What a receiver actually is A Go method is written as a function with an extra parameter list in front of the name: ```go func (v Vec2) Length() float64 { return math.Hypot(v.X, v.Y) } ``` The `(v Vec2)` part is the **receiver**. It is a parameter like any other: it has a name, it has a type, and it is in scope inside the body. The compiler gives it no special treatment beyond deciding which type the method is attached to. That single fact explains every convention below — there is nothing magic to name, only a parameter. ## The convention: one or two letters, from the type Go names the receiver with one or two lowercase letters, almost always taken from the type name. `Buffer` gets `b`, `Client` gets `c`, `Vec2` gets `v`, `Server` gets `s` or `srv`. The standard library is consistent about this: methods on `bytes.Buffer` use `b`, methods on `time.Time` use `t`. The reasoning is scope-based. A receiver lives for the length of one method — often three lines. A name's job is to let a reader hold it in their head for exactly that long, and a single letter does that as well as a word while keeping expressions like `v.X + w.X` readable. Long names buy nothing in a scope that small and cost horizontal space in every line that mentions them. ## The stronger rule: the same name in every method The naming rule most often raised in review is not the length but the **consistency**. A type with thirty methods should use one receiver name in all thirty. Imagine a units-and-geometry package where a `Path` type carries thirty methods, all naming the receiver `p`, and a new contributor adds the thirty-first calling it `path` (or `pt`, or `self`). Nothing breaks, but three things get worse: - **Reading.** The type now has two words for the same thing, and every reader has to notice that they mean the same thing. - **Moving code.** A block lifted from one method into another compiles only after a rename; with one name it just works. - **Reviewing and grepping.** `p.length` finds the writes to a cached field; `path.length` does not, and neither search finds both. This is enforced by people, not by tools. `gofmt` reformats layout and never renames identifiers, and `go vet` has no receiver-naming check at all. ## Why not `this` or `self` Neither word is a Go keyword, so `func (this *Client) Do()` compiles. The objection is that Go is not class-based. There is no implicit receiver, no hidden variable injected into the body, no rebinding at call time, and no inheritance chain for a `this` to travel up. Writing `this` invites a reader to assume machinery that is not there and hides the plainest fact about Go methods: the receiver is the first argument, spelled differently. It also stops being a mnemonic — `this` tells you nothing about the type, while `c` in a file full of `Client` methods does. ## Two practical traps **Shadowing.** A receiver named after a common package hides that package inside the method. Name a receiver `url` and `url.Parse` no longer refers to `net/url` in that body; name it `path` and the `path` package is gone. Short abbreviations sidestep this almost entirely. **Name collisions with parameters.** The receiver shares a scope with the method's parameters, so `func (v Vec2) Add(v Vec2) Vec2` does not compile — `v` is declared twice. This is a real reason to keep the receiver's letter distinct from the parameter letters you tend to use (`v` and `w`, `c` and `req`). ## What the name does and does not affect The receiver name is not part of the exported API: renaming it breaks no caller, because callers never write it. It is, however, visible in generated documentation, which prints the full signature including the receiver — so a stray `self` among twenty-nine `p`s is public-facing sloppiness even though it is not a contract change. And when the body never uses the receiver at all, Go lets you leave it unnamed and write only the type. The summary a reviewer wants to hear: short, derived from the type, lowercase, identical across every method of the type, never `this` or `self`, and never a word that shadows something you use inside the method.

  • Does the receiver name form part of the package's public API?
    No. Callers never write the receiver name, so renaming it breaks nothing and needs no version bump. It is visible in generated documentation, which prints the whole signature including the receiver, so an inconsistent name is still something readers of the docs see.
  • Is the receiver name ever meaningful to the compiler at all?
    Only as a parameter name. It occupies the same scope as the method's parameters, so a parameter cannot reuse it, and it shadows any package-level or imported identifier of the same name inside the body — which is why a receiver called `url` or `path` is a genuine hazard rather than only a style problem.
  • A type has thirty methods using receiver `p`; you add one using `path`. Why do reviewers push back?
    Because the type now has two names for the same value. Readers must reconcile them, a search for `p.length` misses the new method, and code moved between methods needs renaming first. Nothing in the toolchain complains, so review is the only place this is caught.

It is like using one abbreviation for a column across every page of a table: the reader learns it once on page one and never re-reads the legend.

saying these in an interview costs you the question

  • Calls the receiver this or self, as in class-based languages
  • Uses a different receiver name in each method of one type
  • Spells out the type name, as in func (customer *Customer)
  • Believes Go injects a hidden receiver variable into method bodies
  • Thinks renaming a receiver is a breaking API change