Explain method sets. Why does var s Speaker = Dog{} fail to compile?
Question 28HardGo 1.22 to 1.25
type Speaker interface{ Speak() string }
type Dog struct{ name string }
func (d *Dog) Speak() string { return d.name + ": woof" }
func main() {
// var s Speaker = Dog{"rex"} // compile error: Dog does not implement Speaker
// // (method Speak has pointer receiver)
var s Speaker = &Dog{"rex"} // OK
d := Dog{"max"}
fmt.Println(d.Speak()) // OK: d is addressable, so Go rewrites it as (&d).Speak()
fmt.Println(s.Speak())
}
Method sets:
- The method set of
Tcontains only methods with value receivers. - The method set of
*Tcontains methods with value receivers and pointer receivers.
Interface satisfaction is decided by the method set. The reason: an interface stores a copy of a T value, and that copy is not addressable. If Go allowed pointer methods to be called on it, they would change the copy, not your original, so the language forbids it. Calling d.Speak() on a variable works only because d is addressable and the compiler inserts &d for you.
Guidelines:
- Be consistent: if any method needs a pointer receiver (to modify state, or because the type is large or holds a mutex), make all methods pointer receivers.
- A compile-time check that a type implements an interface:
var _ Speaker = (*Dog)(nil).
More on Language Fundamentals & Types
- Q26How does struct and array equality work? What is surprising about NaN as a map key?
- Q27What does the
comparableconstraint mean in generics, and what changed in Go 1.20? - Q29What is addressability, and why can't you call a pointer method on a map element?
- Q30How does struct embedding affect method sets and name resolution?
- Q31Why is an interface holding a nil pointer not equal to nil? What does this print?
- Q32What is the difference between a defined type (
type A B) and an alias (type A = B)?