Go

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 T contains only methods with value receivers.
  • The method set of *T contains 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

All 39 Language Fundamentals & Types questions