skip to content

Promotion and Shadowing

Embedding an anonymous field promotes its fields and methods onto the outer type, which is Go's way of reusing behavior without inheritance. Interviewers probe the resolution rules — what shadows what, and what happens when two embedded types offer the same name at the same depth.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

questions

4

In Go, what does embedding a struct type as an anonymous field promote to the outer struct?

level: juniorimportance: must knowfreq 78%

answer

  1. a field written with no name
  2. the type name becomes the field name
  3. selectors reach one level in
  4. fields and methods, not just methods
  5. d.Draw() is really d.Grid.Draw()

basics

~20 s

Writing 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 s

An 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 lines
go
type 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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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
open as a page

In a Go struct that embeds two types both declaring Draw, how is d.Draw() resolved?

level: middleimportance: should knowfreq 60%

basics

~20 s

Go takes the shallowest match: a name declared on the outer struct shadows a promoted one. When two embedded types tie at the same depth, the short selector is illegal and the compiler reports it as ambiguous where you use it.

open as a page

When should an exported Go type forward to a named field explicitly instead of embedding it?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Embed only when the embedded type's whole surface belongs in your API. Promotion publishes all of it, including surprises such as Lock and Unlock or a MarshalJSON that hijacks encoding. Otherwise keep an unexported field and forward the methods you mean.

open as a page

In Go, what changes when a struct embeds a pointer type or an interface instead of a struct value?

level: middleimportance: nice to knowfreq 38%

basics

~20 s

Promotion works the same, but the zero value does not: an embedded pointer or interface field starts nil, so a promoted call panics until you set it. Embedding an interface also makes the outer type satisfy that interface with no code.

open as a page