Golang Defer a closure

Viewed 530

I am new to go and learning defer. I am wondering why the first one works while the second one not. I wonder what's the difference between them. First:

func say(s string) {
    defer func() {
        if  r := recover(); r != nil {
            fmt.Println("Recovered in cleanup:", r)
        }
        wg.Done()
    }()
    for i := 0; i < 3; i++ {
        fmt.Println(s)
        time.Sleep(time.Millisecond * 100)
        if i == 2 {
            panic("oh")
        }
    }
}

Second:

func cleanup(){
        if  r := recover(); r != nil {
            fmt.Println("Recovered in cleanup:", r)
        }
}

func say(s string) {
    defer func() {
        cleanup()
        wg.Done()
    }()
    for i := 0; i < 3; i++ {
        fmt.Println(s)
        time.Sleep(time.Millisecond * 100)
        if i == 2 {
            panic("oh")
        }
    }
}
3 Answers

The specification says:

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

...

  • recover was not called directly by a deferred function.

The cleanup() function is called by the anonymous deferred function. Rewrite the code to defer cleanup() directly.

func say(s string) {
    defer wg.Done()
    defer cleanup() // cleanup called directly by defer
    for i := 0; i < 3; i++ {
        fmt.Println(s)
        time.Sleep(time.Millisecond * 100)
        if i == 2 {
            panic("oh")
        }
    }
}

As per specification https://golang.org/ref/spec#Handling_panics

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.

In your case recover was not directly called by deferred fuction.

From https://golang.org/pkg/builtin/#recover, we have the following (emphasis mine):

The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.

Related