How to return a value in a Go function that panics?
Asked Answered
S

3

29

My Go function is expected to return a value, but it may panic when calling a library function. I can use recover() to capture this in a deferred call, but how can I return a value in this case?

func MyFunc() string{
    defer func() {
        if err := recover(); err != nil {
            // What do I do to make MyFunc() return a value in case of panic?
        }
    }()
    SomeFuncThatMayPanic()
    return "Normal Return Value"
    // How can I return "ERROR" in case of panic?
}
Susiesuslik answered 16/10, 2015 at 9:45 Comment(0)
L
60

You can use named result parameters. Name your return values, and in the deferred function when panic was detected, you can change the values of the return "variables". The changed new values will be returned.

Example:

func main() {
    fmt.Println("Returned:", MyFunc())
}

func MyFunc() (ret string) {
    defer func() {
        if r := recover(); r != nil {
            ret = fmt.Sprintf("was panic, recovered value: %v", r)
        }
    }()
    panic("test")
    return "Normal Return Value"
}

Output (try it on the Go Playground):

Returned: was panic, recovered value: test

This is mentioned in the Spec: Defer statements:

For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.

It is also mentioned in the blog post Defer, Panic and Recover:

Deferred functions may read and assign to the returning function's named return values.

And also in Effective Go: Recover:

If doParse panics, the recovery block will set the return value to nil—deferred functions can modify named return values.

Lantana answered 16/10, 2015 at 9:51 Comment(2)
I was interested in what would be returned if there were no named return values. Playing with your playground, it looks like it's the zero value of the typeSiren
@KyleChadha Yes, if results are unnamed and a return statement is not reached (e.g. due to a panic), the zero values of the result types are returned. This is detailed in Why can a normal return hide a panic that a named return correctly provides to the caller?Lantana
C
2

Using the example in icza's answer, and if you wonder what happens if you recover from the panic but don't assign the value for the named return, like this:

func main() {
    fmt.Println("Returned:", MyFunc()) // false
}

func MyFunc() (ret bool) {
    defer func() {
        if r := recover(); r != nil {
        }
    }()
    panic("test")
    return true
}

The function will return the zero value of the specified return type.

Run the example in the Go Playground

Cheerleader answered 9/5, 2019 at 20:38 Comment(0)
M
1

You can use named parameter like this:

func main() {
    fmt.Println("Returned:", MyFunc())
}

func MyFunc() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err=convertRecoverToError(r)
        }
    }()
    panic("test")
    return nil
}

func ConvertRecoverToError(r interface{}) error {
    switch x := r.(type) {
    case string:
        return errors.New(x)
    case error:
        return x
    default:
        return errors.New(fmt.Sprint(x))
    }
}
Moonlit answered 27/3, 2022 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.