How does struct embedding affect method sets and name resolution?
Question 30HardGo 1.22 to 1.25
Embedding promotes the embedded type's fields and methods to the outer type. For a struct S:
- If
SembedsT: the method set ofSgetsT's value methods. The method set of*Sgets bothT's and*T's methods. - If
Sembeds*T: bothSand*SgetT's and*T's methods.
type Logger struct{}
func (l *Logger) Log(msg string) { fmt.Println(msg) }
type Service struct{ Logger } // embeds the value type
type Loggable interface{ Log(string) }
var _ Loggable = &Service{} // OK
// var _ Loggable = Service{} // compile error: Log has a pointer receiver on Logger
type A struct{}
func (A) Hello() string { return "A" }
type B struct{}
func (B) Hello() string { return "B" }
type C struct{ A; B }
// C{}.Hello() // compile error: ambiguous selector
fmt.Println(C{}.A.Hello()) // OK: say which one explicitly
type D struct{ A }
func (D) Hello() string { return "D" } // the shallower method shadows A.Hello
Key points:
- Embedding is composition, not inheritance. A promoted method's receiver is the inner value. It knows nothing about the outer type, so there is no virtual dispatch or "override" that the inner code can see.
- A name at a shallower depth wins. The same name at the same depth is ambiguous, which is an error only if you actually use it.
- Embedding a
sync.Mutexin an exported type makesLockandUnlockpart of your public API. Use a named field instead.
More on Language Fundamentals & Types
- Q28Explain method sets. Why does
var s Speaker = Dog{}fail to compile? - Q29What is addressability, and why can't you call a pointer method on a map element?
- 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)? - Q33Arrays vs slices as values: what does this print?
- Q34How does Go handle integer overflow, division, and conversions between numeric types?