A common mistake is to assume that returning a pointer to a local variable leaves it pointing into a finished call. In Go, each goroutine has its own growable, last-in-first-out stack. Entering a function creates a stack frame for that call; returning makes its stack-resident locals inaccessible, even if their bytes have not yet been overwritten. section 12.5.1
func main() {
a := 3
b := 2
c := sumValue(a, b) #1
println(c) #2
}
//go:noinline #3
func sumValue(x, y int) int {
z := x + y
return z
}
Trace the calls: main has a frame for a and b; calling sumValue adds a frame for x, y, and z. After sumValue returns, execution continues in main and its frame remains valid. Inlining is disabled so the call and its frame can be observed. section 12.5.1
A frame's end does not make a returned pointer invalid. If the compiler cannot prove that a local variable is no longer referenced after return, escape analysis allocates it on the heap instead. Passing pointers to variables in a caller's still-active frame down to a called function does not, by itself, require those variables to escape. section 12.5.1 section 12.5.2
func main() {
a := 3
b := 2
c := sumPtr(a, b)
println(*c)
}
//go:noinline
func sumPtr(x, y int) *int { #1
z := x + y
return &z
}
Unlike sumValue, sumPtr returns &z. Because z must remain valid after sumPtr returns, it cannot remain solely in that call's stack frame. section 12.5.1
This distinction matters when optimizing allocations: stack allocations are inexpensive, while heap allocations require garbage-collector work. Do not infer allocation solely from the shape of a call or pointer; inspect compiler escape decisions with go build -gcflags "-m=2" when the distinction matters. section 12.11 section 12.5.2







