Go: returning from defer

GoReturnDeferredPanic

Go Problem Overview


I want to return an error from a function if it panics (in Go):

func getReport(filename string) (rep report, err error) {
	rep.data = make(map[string]float64)

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered in f", r)
			err, _ = r.(error)
            return nil, err
		}
	}()
	panic("Report format not recognized.")
    // rest of the getReport function, which can try to out-of-bound-access a slice
    ...
} 

I appear to have misunderstood the very concept of panic and defer. Can anybody enlighten me?

Go Solutions


Solution 1 - Go

In a deferred function you can alter the returned parameters, but you can't return a new set. So a simple change to what you have will make it work.

There is another problem with what you wrote, namely that the you've paniced with a string but are expecting an error in your type assertion.

Here is a fix for both of those (Play)

defer func() {
	if r := recover(); r != nil {
		fmt.Println("Recovered in f", r)
		// find out exactly what the error was and set err
		switch x := r.(type) {
		case string:
			err = errors.New(x)
		case error:
			err = x
		default:
			err = errors.New("Unknown panic")
		}
		// invalidate rep
		rep = nil
		// return the modified err and rep
	}
}()

Solution 2 - Go

have a look at this

package main

import "fmt"

func iWillPanic() {
	panic("ops, panic")
}
func runner() (rtnValue string) {
	rtnValue := ""
	defer func() {
		if r := recover(); r != nil {
			// and your logs or something here, log nothing with panic is not a good idea
			rtnValue = "don't panic" // modify the return value, and it will return
		}
	}()
	iWillPanic()
	return rtnValue
}

func main() {
	fmt.Println("Return Value:", runner())
}

Solution 3 - Go

func TestReturnFromPanic(t *testing.T) {
   fn := func(filename string) (rep string, err error) {
	   defer func() {
		   if r := recover(); r != nil {
			   err = fmt.Errorf("panic in getReport %s", r)
		   }
	   }()
	   return filename[100:], nil
   }
   t.Log(fn(``))
}

The named return parameter err is the trick.

https://play.golang.org/p/jpaCa9j2iAf

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjrichnerView Question on Stackoverflow
Solution 1 - GoNick Craig-WoodView Answer on Stackoverflow
Solution 2 - Gofang jinxuView Answer on Stackoverflow
Solution 3 - GoZAkyView Answer on Stackoverflow