Go

Does Go do tail-call optimization? What happens with deep recursion, and what does "goroutine stack exceeds 1000000000-byte limit" mean?

Question 555HardGo 1.22 to 1.25

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

All 16 Go Idioms, Design Patterns & Language Design questions