A strong answer
Two compile-and-runtime mechanics govern Go reliability and memory speed: (1) Error Wrapping (%w, Unwrap() error, and Unwrap() []error in Go 1.20+): Wrapping an error with fmt.Errorf("context: %w", err) returns an error whose .Unwrap() method returns err (and errors.Join(e1, e2) returns an error with Unwrap() []error). Never compare errors with err == target or type assertions err.(*MyErr)! errors.Is(err, target) and errors.As(err, &targetPtr) recursively walk the entire .Unwrap() tree to match sentinel values or extract typed error structs at any depth! (2) Escape Analysis (go build -gcflags="-m"): In Go, whether u := &User{} allocates on the fast goroutine stack (freed in 0 ns when the function returns) or on the garbage-collected heap is decided statically by the compiler's Escape Analysis—NOT by whether you wrote &User{} or new(User)! A variable escapes to the heap only if the compiler cannot prove its lifetime is confined to the current stack frame (for example: returning &u up the stack, storing &u in a heap slice/map, or passing u to an interface method whose concrete target isn't inlined).
errors.Is(err, target) and errors.As(err, &out) traverse the full tree of Unwrap() error and Unwrap() []error nodes; a local variable stays on the goroutine stack if and only if the compiler proves no reference to it outlives its defining stack frame. Because stack frames are reclaimed immediately when a function returns (by moving the stack pointer), any pointer returned to a caller (return &local) would dangle on a C stack; Go's compiler proves safety by moving (escaping) any such variable to the garbage-collected heap at compile time.
Allocating a struct on the goroutine stack costs 0 heap allocations and 0 GC work (~1 ns stack pointer bump), whereas escaping to the heap calls runtime.newobject (~25–40 ns allocation + later GC mark-and-sweep cost). In production systems (Go standard library errors (Is, As, Join), database/sql (sql.ErrNoRows), context.DeadlineExceeded, and cmd/compile/internal/escape (go build -gcflags="-m -m").), engineers monitor these exact trade-offs to keep p99 latency predictable and prevent resource exhaustion under peak load.
What the interviewer is listening for
- Traces how error wrapping trees (
%w,errors.Is,errors.As) and compiler Escape Analysis operates in memory step by step rather than giving a vague textbook definition. - States the core correctness invariant and explains why edge cases do not violate it.
- Quantifies time, space, and hardware costs (Return by Value
func() User(<= 64B), Return Pointerfunc() *User(Not Inlined)) with concrete numbers. - Identifies real-world failure modes (Reflexively returning
*SmallStructfrom every constructor (func NewPoint() *Point) thinking pointers are 'faster than copying') and explains how production systems mitigate them.
Common mistakes
- Treating error wrapping trees (
%w,errors.Is,errors.As) and compiler Escape Analysis as a black box without explaining the underlying memory or state transitions. - Ignoring worst-case tail latency or memory overhead (Checking
if err == sql.ErrNoRowsorerr == io.EOFdirectly in application code). - Forgetting boundary conditions and error recovery (Allocating a dynamic slice
make([]byte, runtimeSize)whereruntimeSizeis unknown at compile time (or > 64 KB)).