In Go, what does embedding a struct type as an anonymous field promote to the outer struct?
answer
- a field written with no name
- the type name becomes the field name
- selectors reach one level in
- fields and methods, not just methods
- d.Draw() is really d.Grid.Draw()
basics
~20 sWriting a type inside a struct with no field name embeds it. The embedded type's fields and methods are promoted: you can use them directly on the outer value. The field itself is still named after its type.
solid answer
~40 sAn anonymous (embedded) field is written as a bare type, like `Grid` inside `type Dashboard struct { Grid; Title string }`. Every field and method of `Grid` is promoted, so `d.Rows` and `d.Draw()` work on a `Dashboard` even though neither is declared there. Promotion is purely a selector shortcut: the embedded value is a real field whose implicit name is the unqualified type name, so `d.Grid.Rows` is the same storage as `d.Rows`, and you can pass `d.Grid` wherever a `Grid` is wanted. Promoted methods also join the outer type's method set, which is how a `Dashboard` can satisfy an interface it never explicitly implements. Nothing is copied or generated: it is composition with a shorter spelling, not a base class.
code
go · 16 linestype Grid struct {
Rows, Cols int
}
func (g Grid) Draw() {}
type Dashboard struct {
Grid // anonymous field, implicitly named Grid
Title string
}
func setup() {
var d Dashboard
d.Rows = 24 // same storage as d.Grid.Rows
d.Draw() // same call as d.Grid.Draw()
}go deeper
Be ready to write the declaration from memory and say what d.Rows and d.Draw() resolve to. Know that the field is named after its type, so d.Grid also works.
Explain that promotion is selector sugar over a real field, that promoted methods join the outer type's method set, and that composite literals still need the type name as the key.
Show judgment about what promotion publishes: everything the embedded type exports becomes part of your type's surface, and that surface grows when the embedded type gains a method.
Frame embedding as a coupling decision on a package boundary you cannot easily retract, and set a team convention for when composition is spelled with an embedded field versus a named one.
## What an anonymous field is A Go struct field is normally written as `name Type`. If you write only the type, the field is *embedded* and is called an anonymous field: ```go type Grid struct { Rows, Cols int } func (g Grid) Draw() { /* paint cells */ } type Dashboard struct { Grid // embedded: an anonymous field Title string // an ordinary named field } ``` `Dashboard` has two fields. The second is `Title`. The first is not nameless — it takes the *unqualified type name* as its implicit field name, so it is called `Grid`. That naming rule is worth memorising because it also decides the name for qualified and pointer types: embedding `bytes.Buffer` gives a field named `Buffer`, and embedding `*Grid` gives a field named `Grid`. ## What promotion actually does Because the field exists, `d.Grid.Rows` is an ordinary selector chain. Promotion is the rule that lets you write the shorter form when there is no ambiguity: ```go var d Dashboard d.Rows = 24 // same storage as d.Grid.Rows d.Draw() // same call as d.Grid.Draw() d.Title = "cpu" // an ordinary field, no promotion involved ``` Both *fields* and *methods* are promoted. A promoted method is not a wrapper the compiler generates around a copy; the receiver of `Draw` is the embedded `Grid` value living inside `d`, so a pointer-receiver method promoted from an embedded value mutates the real inner field, not a copy. Promoted methods also become part of the outer type's method set. That is why embedding is Go's usual way to acquire an interface implementation without writing one: a struct that embeds a type already implementing `io.Reader` satisfies `io.Reader` itself. ## The embedded field is still a value you can use Because the embedded field is a real field with a real name, all of these are legal and mean what they say: ```go d.Grid = Grid{Rows: 24, Cols: 80} // assign the whole embedded value render(d.Grid) // pass it where a Grid is wanted d2 := Dashboard{Grid: Grid{Rows: 24}, Title: "cpu"} ``` Note the composite literal: you must use the type name as the key. There is no way to set an embedded field positionally-by-inner-field; `Dashboard{Rows: 24}` does not compile, because `Rows` is not a field of `Dashboard` for the purposes of a literal even though the selector `d.Rows` works. ## Export rules are unchanged Promotion does not launder visibility. Unexported fields and methods of the embedded type are promoted, but an unexported name is still only usable inside the package that declared it. If your package embeds a type from another package, you gain shortcut access to exactly the exported names you could already reach through the field. Conversely, embedding an *unexported* type in an *exported* struct still promotes that type's exported methods to importers, which is a common way to give callers a method set without giving them the type. ## Where the shortcut stops Promotion reaches through as many levels of embedding as you have, choosing the shallowest match. It stops in three places worth knowing early: - **Composite literals**, as above: keys are the outer type's own field names, including the embedded type name. - **Ambiguity**: if two embedded types at the same depth both offer the name, the short selector is not allowed and you must say which one you meant. - **Shadowing**: a field or method declared directly on the outer struct wins over a promoted one of the same name, silently. ## It is composition, not a class hierarchy The mental model that survives contact with Go is: *the outer struct contains a value, and the compiler saves you some typing when you reach into it.* No subtype relationship is created. A `Dashboard` is not assignable to a `Grid` parameter — you pass `d.Grid`. A function that takes a `Grid` cannot be handed a `Dashboard`. Everything promotion gives you is a shorter spelling of a selector plus membership in the outer type's method set, and knowing that one sentence answers most follow-up questions about it.
- What is the embedded field's name, and how do you refer to it explicitly?It takes the unqualified type name. Embedding `Grid` gives a field named `Grid`; embedding `bytes.Buffer` gives `Buffer`; embedding `*Grid` still gives `Grid`. So `d.Grid`, `d.Buffer` and composite-literal keys like `Dashboard{Grid: Grid{Rows: 24}}` all work, and that is how you reach a promoted name that has been shadowed or is ambiguous.
- Does embedding make a Dashboard usable where a Grid is expected?No. Embedding creates no subtype relationship and no conversion: a function taking a `Grid` will not accept a `Dashboard`, you pass `d.Grid`. What the outer type does gain is the promoted methods in its method set, so it can satisfy an *interface* that `Grid` satisfies without declaring any methods itself.
- How does encoding/json treat an embedded struct's promoted fields?By default they are flattened: the embedded struct's exported fields are encoded as if declared on the outer struct, so `{"Rows":24,"Title":"cpu"}` rather than a nested object. Giving the embedded field a JSON name tag turns it back into a nested object under that name. Conflicting names at the same depth are dropped.
It is like a desk drawer that is part of the desk: reaching for the stapler on the desk finds the one in the drawer, but the drawer is still a distinct thing you can open and hand over on its own.
saying these in an interview costs you the question
- Says the embedded field has no name at all
- Thinks promotion copies the inner fields into the outer struct
- Claims only methods are promoted, not fields
- Believes promotion makes unexported names visible to importers
- Writes Dashboard{Rows: 24} in a composite literal and expects it to compile
- Assumes a Dashboard can be passed where a Grid is required