Why does a T value fail to satisfy a Go interface whose methods have pointer receivers?
answer
- two method sets, not one
- the pointer gets both kinds
- the interface stores a copy
- a copy has no address to take
- one ampersand at the assignment
basics
~20 sGo's method set of T contains only methods declared with receiver T; methods declared with receiver *T belong to *T's method set alone. Interface satisfaction is checked against the method set, so only the pointer satisfies the interface.
solid answer
~50 sGo gives every type two method sets. The method set of `T` holds exactly the methods declared with receiver `T`; the method set of `*T` holds those *and* the ones declared with receiver `*T`. Interface satisfaction is defined on method sets, so if a backend declares `func (b *fileBackend) Close() error`, then `*fileBackend` implements the interface and `fileBackend` does not - the compiler says the type "does not implement Backend (method Close has pointer receiver)". The fix at the assignment is to store `&fileBackend{}` instead of `fileBackend{}`. The asymmetry is not arbitrary: an interface holds a copy of the value assigned to it, that copy lives inside the interface and is not addressable, so there would be no `*fileBackend` for the method to receive. Rather than mutate an invisible copy, Go makes it a compile error.
code
go · 27 linestype Backend interface {
Open(name string) error
Close() error
}
type fileBackend struct {
path string
}
func (b *fileBackend) Open(name string) error {
b.path = name
return nil
}
func (b *fileBackend) Close() error {
return nil
}
var registry = map[string]Backend{}
func register() {
// rejected: fileBackend's method set holds neither Open nor Close
registry["file"] = fileBackend{}
// accepted: *fileBackend's method set holds both
registry["file"] = &fileBackend{}
}go deeper
Recall the two-line rule: T's method set has value-receiver methods only, *T's has both. When you see "method has pointer receiver", store the address instead of the value.
Explain why the rule exists: the interface copies the value, that copy is not addressable, so the pointer-receiver method could only ever mutate something invisible. Contrast that with the call-site shorthand on a variable.
Show where this actually surfaces - registries, slices of interfaces, constructors returning an interface - and how you keep a codebase from tripping over it repeatedly rather than patching one ampersand at a time.
Own the API consequence: whether your package hands out a concrete value or a pointer decides what callers can put into interfaces, and changing that later breaks every call site that stored one.
## The rule, stated exactly Go's specification defines a **method set** for each type, and interface satisfaction is defined in terms of it: - The method set of a type `T` consists of all methods **declared with receiver type `T`**. - The method set of the pointer type `*T` consists of all methods declared with receiver `T` **or** `*T`. So `*T` is the richer type: it can do everything `T` can do, plus the pointer-receiver methods. A type satisfies an interface when the interface's methods are all present in that type's method set. The consequence people meet first is this: if even one of the interface's methods is declared with a pointer receiver, a plain `T` value cannot be assigned to that interface, and the compiler reports something of the shape ``` cannot use fileBackend{} (value of type fileBackend) as Backend value: fileBackend does not implement Backend (method Close has pointer receiver) ``` That parenthesised clause is the tell. It does not say the method is missing - it says the method exists but on the *other* type. ## Why the asymmetry exists An interface value stores two words: the dynamic type and the value (or a pointer to it). When you write `var b Backend = x`, the value `x` is **copied** into the interface. That copy has no name you can take the address of; the language deliberately gives you no way to obtain a pointer to the value inside an interface. A pointer-receiver method needs a `*fileBackend`. If Go silently took the address of the copy, `Close` would mutate a value nobody else can reach and every write would vanish. So the rule converts a subtle run-time bug into a compile-time error. This also explains why the reverse direction is fine. Storing `&fileBackend{}` in the interface copies the *pointer*, and a copy of a pointer still points at the same struct, so both value-receiver methods (the compiler dereferences) and pointer-receiver methods work. ## Why calling the method directly still compiles The most confusing part for someone new is that this compiles: ```go b := fileBackend{} b.Close() // fine ``` A local variable is **addressable**, and for an addressable operand `b.Close()` is shorthand the compiler rewrites as `(&b).Close()`. That shorthand is a convenience at the *call site*; it does not add `Close` to `fileBackend`'s method set. Interface satisfaction never uses the shorthand, because there is no addressable operand involved - the value has already been copied into the interface. This is why "but it works when I call it directly!" is not evidence against the compiler. ## Mixed receivers A type may declare some methods with a value receiver and others with a pointer receiver. Then: - `*T` still satisfies any interface made from those methods, because its method set holds both kinds. - `T` satisfies the interface **only if every** method the interface lists is declared with a value receiver. So `*T` is the safe answer whenever you are not sure. A common convention is to keep one receiver form for the whole type so this question never has to be asked, but the language does not require it. ## The three ways out When the compiler rejects your assignment you have exactly three moves: 1. **Pass the pointer.** `registry["file"] = &fileBackend{}`. This is the usual answer, and often the only correct one, because a type with pointer receivers usually has them for a reason - it carries state that methods change. 2. **Declare the methods with value receivers.** Legitimate only when no method needs to mutate the receiver and the struct is cheap to copy. 3. **Wrap.** Define a separate type whose value-receiver methods delegate to a pointer it holds. Rarely worth it; mentioned so you recognise it in a codebase. ## Where it bites in real code The error rarely appears next to the type. It appears where a value is put *into* an interface: filling a `map[string]Backend` registry, appending to a `[]Backend`, returning a concrete type from a function declared to return the interface, or passing a struct to a function taking an interface parameter. Someone adding a new implementation writes a struct literal, the registry file lights up, and the message names a method they did not touch. Reading the parenthesis - "method Close has pointer receiver" - is the whole diagnosis; the fix is one `&`. ## What it is not It is not about exported versus unexported names, not about nil, and not a performance rule. It is one line of the specification about which methods belong to which type, plus the fact that an interface holds a copy.
- Calling that same method on a local variable of the value type compiles. Why does that not make the value satisfy the interface?A variable is addressable, so `b.Close()` is shorthand the compiler rewrites as `(&b).Close()`. That is a call-site convenience on an addressable operand. Interface satisfaction is checked against the method set instead, and no shorthand applies there, because the value has already been copied into the interface where nothing can take its address.
- If a type declares some methods with value receivers and some with pointer receivers, which of T and *T satisfies the interface?`*T` does, always - its method set holds both kinds. `T` satisfies the interface only if every method the interface lists happens to be declared with a value receiver. Mixing is legal, but only the pointer is guaranteed to work, which is why most codebases pick one receiver form per type.
- You have an interface value you know holds a T value rather than a *T. Can you type-assert it and call a pointer-receiver method?No. The result of a type assertion is not addressable, so the compiler cannot take its address. You can assign it to a variable first and then call the method, but you are then mutating your own copy - the value still sitting in the interface is untouched. If you need mutation, put a pointer in the interface to begin with.
saying these in an interview costs you the question
- Says Go silently takes the address to satisfy the interface
- Claims T and *T always share one method set
- Reads the error as the method being missing entirely
- Thinks writing &x at the call site fixes the assignment
- Says pointer receivers here are only a performance choice
- Believes a type conversion can add methods to a value type