Does Go do tail-call optimization? What happens with deep recursion, and what does "goroutine stack exceeds 1000000000-byte limit" mean?
No. The Go compiler does not guarantee tail-call elimination and in practice doesn't do it, partly to keep stack traces complete. Each recursive call uses a stack frame. Goroutine stacks start small (a few KB) and grow dynamically: when a function prologue detects there isn't enough room, the runtime allocates a stack twice the size, copies the old one, and adjusts pointers. That makes deep recursion workable up to a limit.
The maximum stack size is 1 GB on 64-bit platforms (250 MB on 32-bit). Past it, the runtime prints runtime: goroutine stack exceeds 1000000000-byte limit followed by fatal error: stack overflow. This is a fatal error, not a panic: recover cannot catch it, and the whole process dies with a stack trace.
func depth(n int) int { return depth(n+1) } // unbounded: fatal stack overflow
// Fix: convert to iteration with an explicit stack
func sumTree(root *Node) int {
total, stack := 0, []*Node{root}
for len(stack) > 0 {
n := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if n == nil {
continue
}
total += n.Val
stack = append(stack, n.Left, n.Right)
}
return total
}
Gotchas: debug.SetMaxStack can change the limit. A common real-world cause is accidental recursion, such as a String() method that calls fmt.Sprintf("%v", t) on its own type, or MarshalJSON calling json.Marshal(t) (fix with a type alias like type plain T). Stack copying also means you should never hold raw uintptr addresses to stack variables.
More on Go Idioms, Design Patterns & Language Design
- Q553Why does Go have no inheritance, function overloading, default arguments, ternary operator or exceptions? How do idiomatic Go programs work around each?
- Q554Can you compare functions in Go? Why is a func only comparable to nil, and how does that affect using funcs as map keys?
- Q556How do you structure a large Go project (cmd/, internal/, pkg/ debate)? How should packages be named and split to avoid stutter and cycles?
- Q557How do you do dependency injection in Go? Compare manual constructor wiring with google/wire and uber/fx.
- Q558How would you implement the decorator, strategy, and adapter patterns idiomatically in Go?
- Q559How do you implement a circuit breaker and bulkhead in Go? What state machine and concurrency concerns are involved?