compare Method Sets with an alternative
Fast recall
Quick revision
Core answer
Practical understanding of Method Sets.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The receiver clause binds a function to a type:
func (u User) Greet() string { ... }
// ^ receiver: this method now belongs to UserThe one rule that governs where methods can be declared: the receiver's base type must be defined in the SAME PACKAGE as the method. You can extend your own types; you cannot open someone else's — no adding HeadCount() to []string or io.Reader.
The reason is coherence: method sets are a compile-time contract per package. If any package could add methods to any type, then io.Reader could have a different method set in every program — interface satisfaction would depend on import graphs, compile determinism would die, and two packages adding the same method name would conflict with no resolution rule. Go closes the whole problem at the root.
The idiom that replaces 'just add a method': the wrapper type — define a new type in YOUR package with the underlying type, and attach methods there:
type UserIDs []string // new type, your package
func (u UserIDs) Dedupe() UserIDs { ... } // your methods on ittype UserIDs []string is a NEW type (not an alias) — it has []string's underlying structure and zero of its methods (a defined type starts with an EMPTY method set). That's deliberate: the wrapper doesn't inherit []string's methods either — it's a fresh surface, yours to build.
The alias (type U = User — Go 1.9+) is the other direction: SAME type, both names usable interchangeably — aliases for compatibility and refactoring, not for method attachment (there's nothing to attach — it's one type).
compare Method Sets with an alternative
Direct Answer
Methods are declared ONCE, attached to a receiver type defined in the same package. You cannot add methods to types from other packages — that's why wrapper types exist.
Deep Dive & Architecture
The same-package rule's exact boundary: 'the receiver type must be defined in the same package as the method declaration' — the SPEC says its underlying type must not be a pointer or interface type and must be declared in the same package. The interesting edges:
- You CAN define methods on named types whose underlying type comes from elsewhere: type MyList = ... no — type MyList []int (defined in your package, underlying []int is builtin): methods attach to MyList freely. The rule is about WHERE THE TYPE IS DEFINED, not where its structure came from.
- Embedding partially reopens extension: embed []*User in your type and your type has promoted methods... only the ones on []*User (which its package already defined). You route to existing methods; you add none to the inner.
- Generic type parameters (1.18+): methods on generic types are declared with the receiver carrying the parameterization: func (l List[T]) Push(v T) — the method is on List[T], your defined type, same-package rule unchanged.
The empty-method-set property of defined types deserves its own note, because it surprises: type MyReader io.Reader — defined type from an interface — has NO methods (not even Read). Its method set is empty until you build it — you're expected to embed io.Reader INSIDE it (the delegation pattern) if you want forwarding. The one exception in the language: embedded interfaces' methods promote into the outer STRUCT — but that's struct embedding, a different mechanism from type definition.
Why the design chose this over extension methods (C#) or categories (Objective-C / Rust traits): Go's method set must be closed — visible from the package alone — for separate compilation (compile a package knowing its types' full surfaces without reading the universe), for interface determinism (satisfaction checkable locally), and for security (no smuggling methods onto types across trust boundaries). The cost — wrapper boilerplate — buys compile determinism, and the ecosystem's shape (small interfaces + adapters + wrappers) grew from that trade.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Method Sets, and when would you use it?
- When would you use Method Sets in a real project?
- What is a common mistake when using Method Sets?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.