Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return a value in a Go function that panics?

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?
}
like image 708
NeoWang Avatar asked Oct 16 '15 09:10

NeoWang


People also ask

How do you return a value in Go?

In Go language, you are allowed to return multiple values from a function, using the return statement. Or in other words, in function, a single return statement can return multiple values. The type of the return values is similar to the type of the parameter defined in the parameter list.

How do I restore panic in Golang?

Recovering from a Panic Recover is useful only when called inside deferred functions. Executing a call to recover inside a deferred function stops the panicking sequence by restoring normal execution and retrieves the error message passed to the panic function call.

What happens when Go routine panics?

A panic is an exception in Go A panic stops the normal execution of a goroutine: When a program panics, it immediately starts to unwind the call stack. This continues until the program crashes and prints a stack trace, or until the built-in recover function is called.

How do you catch panic in Golang?

Suppose a function G defers a function D that calls recover and a panic occurs in a function on the same goroutine in which G is executing. When the running of deferred functions reaches D , the return value of D 's call to recover will be the value passed to the call of panic .


2 Answers

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.

like image 157
icza Avatar answered Sep 21 '22 19:09

icza


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

like image 30
Carlos Fuentes Avatar answered Sep 24 '22 19:09

Carlos Fuentes