skip to content

Structs, Methods and Embedding

How Go attaches behavior to data without classes: methods on any defined type, receivers that are either copies or pointers, and embedding as the composition mechanism. Anyone arriving from Java or Python gets probed here, because the shapes look familiar and behave differently.

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

explore

questions

25

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 Go, does embedding a struct make the outer type usable wherever the embedded type is expected?

level: juniorimportance: must knowfreq 70%

basics

~10 s

No. Embedding is composition, not subtyping: a struct that embeds StateRecord is its own distinct type and is never assignable to StateRecord. You pass the embedded value explicitly, as n.StateRecord.

open as a page

Why does a T value fail to satisfy a Go interface whose methods have pointer receivers?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Go's method set of T contains only methods declared with receiver T; methods declared with receiver *T belong to *T's method set alone. Interface satisfaction is checked against the method set, so only the pointer satisfies the interface.

open as a page

What is a method value in Go, and what does `f := runner.Warmup` capture?

level: juniorimportance: must knowfreq 62%

basics

~20 s

A method value is the function value you get by writing runner.Warmup with no call parentheses. Go evaluates and saves the receiver at that moment, so calling f() later runs Warmup on that saved receiver and takes no receiver argument.

open as a page

In Go, what is the difference between a value receiver and a pointer receiver on a method?

level: juniorimportance: must knowfreq 86%

basics

~20 s

A value receiver gets a copy of the value, so anything the method writes to it is discarded when the method returns. A pointer receiver gets the address of the caller's value, so its writes are visible afterwards.

open as a page

In a Go struct, what does capitalising a field name like Name instead of name control?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A field whose name starts with an upper-case letter is exported: any package that imports the declaring package can read and write it. A lower-case first letter keeps the field usable only inside the package that declares the struct.

open as a page

In Go, why does a promoted method keep calling the embedded type's method instead of the outer struct's redefinition?

level: middleimportance: must knowfreq 62%

basics

~20 s

Because embedding has no virtual dispatch. The promoted method runs with the embedded value as its receiver, and that receiver holds no link back to the outer struct, so calls inside it bind statically to the embedded type's own methods.

open as a page

In Go, why does o.Apply() compile when o is an Order value but Apply has a pointer receiver?

level: middleimportance: must knowfreq 68%

basics

~10 s

Because the compiler rewrites the call. When the operand is addressable, o.Apply() is shorthand for (&o).Apply(); the reverse shorthand turns p.Total() on a pointer into (*p).Total(). Both are call-site conveniences only.

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

Why can't you call a pointer-receiver method on a Go map element or on a function's return value?

level: middleimportance: should knowfreq 60%

basics

~20 s

Calling a pointer-receiver method on a value makes the compiler take that value's address, and only addressable operands have one. Map index expressions and call results are not addressable, so the call is rejected; variables and slice elements are.

open as a page

In Go, why does the method value f := c.Total go stale when Total has a value receiver?

level: middleimportance: should knowfreq 50%

basics

~20 s

Because c.Total is evaluated immediately and a value receiver saves a copy of c beside the function. Later writes go to the original variable, which the saved copy is not connected to, so f keeps returning old values.

open as a page

Why do Go programmers use map[string]struct{} rather than map[string]bool for a set?

level: middleimportance: should knowfreq 46%

basics

~20 s

struct{} is a type with no fields and a size of zero, so the map stores keys and no value payload at all. Membership is then read from the comma-ok form, with no ambiguous stored false to misread.

open as a page

In Go, what does the compiler do with a struct field tag like schema:"user_id"?

level: middleimportance: should knowfreq 58%

basics

~20 s

Almost nothing: it checks the tag is a valid string literal and stores it with the field in the type's information. A tag is inert metadata, and only code that inspects the type at run time gives it any meaning.

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, how do you make an embedded struct's method call the outer type's implementation instead of its own?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Give the embedded type an interface field for the step that varies, and assign the outer value to that field when you construct it. Dispatch then goes through the interface, because embedding alone provides none.

open as a page

A teammate adds a backend to a map[string]Backend registry and hits "method Close has pointer receiver". How do you diagnose and fix it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The registered value is a struct value whose Close is declared on the pointer type, so its method set lacks Close. Register the address instead. To stop it recurring, have each backend package export a constructor that returns the interface.

open as a page

A registry of Go method values built at startup ignores every config reload — why, and how do you fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Each entry was built from a value-receiver method, so it saved a copy of the runner struct as it was at startup. Reload mutates the live struct the copies are disconnected from. Fix it with pointer receivers, or rebuild the registry.

open as a page

In a Go order state machine, Advance leaves the status unchanged after it returns. How do you diagnose that?

level: seniorimportance: should knowfreq 56%

basics

~20 s

Assume the write landed on a copy. Check whether Advance has a value receiver, and whether the call site works on a copy such as a range loop variable. Confirm by printing the receiver's address with %p at entry and comparing it with the caller's.

open as a page

You own an exported Go type other teams import. How do you decide pointer versus value receivers before release?

level: principalimportance: should knowfreq 42%

basics

~20 s

Treat the receiver form as part of the published contract, not a per-method detail. Value receivers tell importers the type is safe to copy; pointer receivers tell them it has identity and must be shared. Decide once, apply it to every method, and document it.

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

In Go, what initializes the embedded part of a struct when there is no base-class constructor?

level: middleimportance: nice to knowfreq 33%

basics

~20 s

Nothing runs. Creating a struct zeroes every field, embedded ones included, so the embedded part gets its zero value. If it needs setup, the outer type's own New function must do it or the composite literal must supply it.

open as a page

What does `var _ Backend = (*fileBackend)(nil)` assert, and how does the value-typed form differ?

level: middleimportance: nice to knowfreq 38%

basics

~20 s

It is a compile-time assertion that *fileBackend implements Backend: a blank-identifier variable of the interface type is initialised with a typed nil pointer, so the package fails to build if a method is missing or declared on the wrong receiver.

open as a page

What does the method expression Point.Add produce in Go, and how does it differ from p.Add?

level: middleimportance: nice to knowfreq 34%

basics

~20 s

Point.Add is a method expression: it yields an ordinary function whose first parameter is the receiver, so its type is func(Point, Point) Point. Nothing is bound. Writing p.Add instead binds p as the receiver and yields func(Point) Point.

open as a page

In Go, can you declare methods on a non-struct type such as type Money int64?

level: middleimportance: nice to knowfreq 40%

basics

~20 s

Yes. Methods attach to any defined type declared in the same package — an integer, a string, a slice, a map or a function type — not only to structs. The receiver base type may not be a pointer type or an interface type.

open as a page

A struct tag reads schema: "user_id" with a space after the colon — why does the Go build still pass?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

A struct tag is an unchecked string literal, so any content compiles. The space breaks the conventional key:"value" grammar, so the key is never found at run time. go vet's structtag check reports malformed tags.

open as a page