Search code examples
gorecoverpanic

recover() behaviour in Go when called inside a deferred nested function


I understand the following point wrt to recover() behaviour in Go. recover() follows immediate evaluation but delayed execution.

So if it is not called inside a deferred function (code snippet 1), the recover() is evaluated then and there itself at which point it is still nil and hence our code panics.

If it is called inside a deferred function, the function call is registered but recover() is called after the panic has occurred and we recover successfully.

package main

import "fmt"

func main() {
    defer recover() // since not called inside a deferred func, it will not recover successfully

    panic("This is a panic")

}

Now keeping this in mind, I wrote the below code.

import (
    "fmt"
)

func main() {

    defer func(){
        customRecover()
    }()

    panic("This is a panic")

}

func customRecover(){
    if r:= recover(); r!= nil{
        fmt.Println("Custom Recovery for: ",r)
    }
}

I searched online for the reasoning and I get the same answer that

The return value of recover is nil if any of the following conditions holds:

  • panic's argument was nil;
  • the goroutine is not panicking;
  • recover was not called directly by a deferred function.

But I'm failing to understand why the 2nd snippet is not working?!?

It satisfies all the 3 conditions strictly speaking. Is there a caveat that I'm missing? I guess the 3rd condition is not satisfied because it is not called directly but via a nested function call. But why does it need to be inside deferred only?

Similar questions: here, here, here and here.


Solution

  • As you stated, the 3rd condition is not satisfied. Recover won't work in a nested function because panic propagates through the call stack "downward" and in the 2nd snippet, the recover() call in the customRecover function is "upward" from the panic perspective, so it doesn't "see" the panic.