skip to content

Why does Go name a getter Owner() instead of GetOwner(), while the setter stays SetOwner()?

level: middleimportance: should knowfreq 54%

answer

  1. the return value already implies getting
  2. a mutation needs a verb
  3. read it at the call site
  4. Get survives where a lookup happens
  5. field and method cannot share a name

basics

~20 s

Go drops Get because a method with a return value already implies it, so the accessor is named for the value it returns: Owner(). Set stays because a mutator needs a verb. Get survives only where a real lookup happens.

solid answer

~40 s

Go names an accessor for the noun it returns: `Owner()`, `Len()`, `Year()`. The `Get` prefix carries no information — the method already returns something, and the call site reads better as `cfg.Owner().Name` than `cfg.GetOwner().Name`. The setter keeps `Set` because a mutating method genuinely needs a verb, and `SetOwner(o)` distinguishes itself from the reader in a way a bare noun could not; the standard library follows this with names like `sql.DB.SetMaxOpenConns` and `os.File.SetDeadline`. `Get` is not banned outright: it is idiomatic where the call performs an actual lookup keyed by an argument or reaches outside the process, as in `http.Header.Get` or `os.Getenv`. One mechanical consequence to know: a Go type cannot have a field and a method with the same name, so the accessor pattern normally keeps the field unexported and exports the method.

code

go · 7 lines
go
type Config struct {
	owner string
}

func (c *Config) Owner() string { return c.owner }

func (c *Config) SetOwner(owner string) { c.owner = owner }

go deeper

for a junior

Remember the shape: the reader is Owner(), the writer is SetOwner(). Be ready to say that Get adds nothing because a method with a return value already implies it.

for a middle

Explain the asymmetry rather than reciting it: a mutator has no return value to signal intent, so it needs the verb. Know that a field and a method cannot share a name, which forces the unexported-field pattern.

for a senior

Show judgment about whether the pair should exist. Argue for an exported field when there is no invariant to protect, and defend keeping Get on methods such as http.Header.Get that really perform a lookup.

for a principal

Own what the accessor boundary buys. Wrapping a field is a commitment to a method signature other teams compile against; decide deliberately which fields are exported surface and which are hidden behind a method you can later change.

## The convention A method that simply returns a value is named for the value, not for the act of getting it. ``` func (c *Config) Owner() string func (c *Config) SetOwner(owner string) ``` Not `GetOwner`. The standard library is consistent about this: `bytes.Buffer` has `Len`, `time.Time` has `Year`, `Month` and `Day`, `http.Request` exposes `Context`. None of them is prefixed. The setter keeps its verb: `SetOwner`, and in the standard library `sql.DB.SetMaxOpenConns`, `sql.DB.SetConnMaxLifetime`, `os.File.SetDeadline`, `http.Request.SetBasicAuth`. The asymmetry is deliberate rather than sloppy, and explaining *why* is the whole content of this question. ## Why Get is dropped and Set is not Go's naming principle is that a name should carry information the reader does not already have. Consider what each part of `x.GetOwner()` tells you: - `Owner` — which value. Informative. - `Get` — that a value comes back. **Already implied**: it is a call with a return value, and the call site is almost always consuming that value. - `()` — that it is a method rather than a field. Visible. So `Get` is pure noise. Removing it also makes the accessor read like the field it stands in for, which matters because in Go a getter is very often a later replacement for a field that used to be exported directly: callers keep writing `cfg.Owner`, now with parentheses, and the shape of the code does not change. `Set` is a different case. A mutating method returns nothing useful, so its name is the only signal about what it does. `Owner(o)` would be indistinguishable at a glance from the reader, and overloading is not available in Go — one name, one method. `Set` is the verb that makes the mutation visible at the call site, and it costs three characters that genuinely carry meaning. ## When Get is legitimate The rule is about *accessors*, not about the letters G-e-t. A `Get` prefix is idiomatic when the method actually performs work rather than handing back a stored field: - `http.Header.Get(key)` and `url.Values.Get(key)` — a keyed lookup over a map with canonicalisation and a defined behaviour for the missing case. - `os.Getenv(name)` and `os.Getpid()` — reaching outside the process to the environment or the OS. What these share is that something happens: a key is resolved, an external source is consulted. If a reviewer asks you to drop `Get` from a method that takes an argument and searches, they are over-applying the rule. ## The field-versus-method constraint A fact that shapes the pattern mechanically: **a Go type cannot declare a field and a method with the same name.** Writing a struct with an exported field `Owner` and also a method `Owner()` is a compile-time error, not a shadowing rule. So the accessor pattern in Go is almost always: ``` type Config struct { owner string // unexported storage } func (c *Config) Owner() string { return c.owner } ``` The field goes lowercase and the method takes the exported name. This is also why Go code so often skips accessors entirely: if there is no invariant to protect and no future substitution planned, the idiomatic move is an exported field `Owner string` and no methods at all. Writing a getter and setter pair around a plain field, out of habit, produces four lines that do nothing — and an interviewer may well probe whether you would write them at all. ## What a reviewer is actually looking for Three things, in order: 1. That you drop `Get` without being told. 2. That you keep `Set` and can say why the asymmetry exists rather than treating it as an inconsistency. 3. That you know when *not* to write the pair. Go does not have a cultural expectation that every field is wrapped. Accessors appear when there is something to enforce — validation, a lock held while reading, a computed value, a field that must not be written after construction — and the absence of that reason is a reason to expose the field. ## Related naming shapes Worth having ready, because follow-ups reach for them: - A method that constructs a modified copy rather than mutating in place is often named `With...` — that is a different pattern from `Set...`, used for options and immutable-style builders, and it returns a value. - A method that may fail returns an error rather than growing a `Try` prefix; there is no `TryOwner` idiom in Go. - A boolean accessor is usually named as a predicate — `IsZero`, `IsDir` — rather than `GetIsDir`, which is the same rule applied to a different part of speech.

  • Why does os.Getenv keep the Get prefix if Go drops it from accessors?
    Because it is not an accessor. `os.Getenv` reaches outside the process to consult the environment, and `http.Header.Get` and `url.Values.Get` resolve a key with canonicalisation and a defined empty-result behaviour. The convention removes `Get` where it adds nothing to a plain field read; it does not forbid the word where real work happens.
  • Would you write an accessor pair at all for a simple configuration field?
    Usually not. Go has no cultural expectation that fields are wrapped, so an exported field is idiomatic unless there is something to enforce — validation, a lock held while reading, a computed value, or immutability after construction. Writing `Owner()`/`SetOwner()` around a bare string adds surface area without adding a guarantee.
  • What stops you from having both an exported field Owner and a method Owner()?
    The compiler. A Go type cannot declare a field and a method with the same name — it is a compile-time error, not a shadowing rule. That is why the accessor pattern stores the value in an unexported field and gives the exported name to the method.

saying these in an interview costs you the question

  • Writes GetOwner out of habit from another language
  • Calls Set inconsistent and drops it too, naming the mutator Owner
  • Wraps every field in a getter and setter pair reflexively
  • Claims Get is banned everywhere, including keyed lookups
  • Thinks a method can shadow a field of the same name