Go

A deferred function panics while another panic is in progress. What prints?

Question 504HardGo 1.22 to 1.25
func main() {
    defer func() {
        fmt.Println("recovered:", recover())
    }()
    defer func() {
        fmt.Println("cleanup")
        panic("second")
    }()
    defer fmt.Println("first defer")
    panic("first")
}

Output:

first defer
cleanup
recovered: second

When panic("first") starts, the deferred calls run LIFO. The second closure panics again. The new panic replaces the first one as the value recover will see, and unwinding continues. The outermost deferred function calls recover() and gets "second". The "first" value is lost to recover, though a crash trace without recover would show both (panic: first followed by panic: second).

Lessons for code review: cleanup code in defers (such as Close or Rollback) must not panic, or it hides the original error. Deferred functions keep running during a panic, so they are the right place to release locks. After a successful recover, the function returns normally with its named results as they are at that point. Use that to set err:

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("recovered: %v", r)
    }
}()

More on Tricky Output & Code-Review Puzzles

All 38 Tricky Output & Code-Review Puzzles questions