debug a problem involving Struct Literal And Fields
Fast recall
Quick revision
Core answer
Practical understanding of Struct Literal And Fields.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The selector chain is the universal access form:
type Address struct{ City, Zip string }
type User struct{ Name string; Address Address }
u := User{Address: Address{City: "Pune", Zip: "411"}}
u.Name = "Al" // field
u.Address.City = "Mumbai" // nested — one chain
p := &u
p.Address.City = "Goa" // pointer — auto-dereferenced, no p->AddressThe pointer rule: Go has no -> operator — the dot works on pointers to structs directly (the compiler inserts the dereference). p.Address.City is (*p).Address.City. This eliminates an entire class of C/C++ syntax decisions and errors — chains are uniform whether the root is a value or a pointer.
The embedded-struct case composes the same way — promotion is field access at depth (admin.Name works because the lookup descends into the embedded User). Deep chains just get longer, not different.
The mutation exceptions — where dot-chains DON'T work for writing:
- 1
Map elements: m["a"].City = "x" — compile error (non-addressable — the map can move entries). The pattern: copy out, modify, store back (u := m["a"]; u.City = "x"; m["a"] = u) — or store *User values in the map so the mutation goes through the shared pointee.
- 2
Function returns and temporaries: same addressability rule — get the value into a variable first.
- 3
Value-receiver method chains: the method's copy is addressable but mutating it doesn't reach the caller — the earlier receiver batch's subject; the selector works, the write doesn't stick.
Reading is almost always fine — the exceptions are exclusively about WRITES needing stable addresses. The one-line summary: reads flow through any chain of values, pointers, and embedded structs; writes need every link addressable.
debug a problem involving Struct Literal And Fields
Direct Answer
Dot chains: user.Address.City. Pointer chains auto-dereference: p.Address.City (no -> in Go). Map fields need the value-copy-out pattern when mutating (non-addressable slots).
Deep Dive & Architecture
The auto-deref rule's mechanics: the selector p.Field compiles to a field access at a fixed offset from the pointer — the same code whether written (*p).Field or p.Field. The compiler knows p's static type (*User) and that . is a struct field selector — the combination means 'load field at offset.' No runtime dispatch, no nil check (a nil p with .Field panics — the nil-deref check is the hardware's, at the dereference). This is why struct field access is the fastest member access in the language — one memory load, statically computed offset.
The selector resolution order, formally, for chains: for a.Field, the compiler searches Field in the type's own fields (depth 0), then embedded types' fields (depth 1), then THEIR embedded (depth 2)... first hit wins; ties at the same depth are ambiguous (usable only via the full path — the embedding batch covered this). The chain u.Address.City is TWO selector resolutions (u → Address field; Address value → City field) — each statically computed. Nothing in the chain is dynamic — struct field access is compile-time-resolved layout arithmetic, every level.
The map-element mutation rule, mechanically, once more because it's the question's most common follow-up: m[k] on a map of struct values returns a COPY (the map stores values by value in its buckets; the lookup can't hand out a stable pointer because growth relocates buckets). m[k] = v compiles (copy IN); &m[k] doesn't (no stable address OUT). The workaround patterns reverse the indirection: map[string]*User stores pointer VALUES — m[k] returns a copy of the pointer (stable, one word), and .City through it writes the shared pointee. That's the canonical fix and the reason pointer-valued maps are the standard shape for mutable-struct collections.
The coda for interviews: 'no -> operator' is the surface observation; the depth is that Go unified value/pointer member access into one syntax with the compiler choosing (insert deref vs direct offset) from static types — which composes with the addressability rules to produce the write exceptions. The selector is simple BY DESIGN — all complexity moved into rules (addressability, method sets) that are checkable at compile time.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Struct Literal And Fields, and when would you use it?
- When would you use Struct Literal And Fields in a real project?
- How would you compare Struct Literal And Fields with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.