ataraskov.dev

About everything and nothing


Don't lose an error during panic

It can be useful to print the error message returned by panic (if one is returned). That should be simple enough. Here were are not concerned that panic can return any, just to keep it simple.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func job() {
	fmt.Println("in job")
	panic(errors.New("error in job"))
}

func executor() error {
	var err error
	fmt.Println("in executor")
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()
	job()

	return err
}

func main() {
	fmt.Println("in main")
	err := executor()
	fmt.Printf("error is %v", err)
}

 

But above example reports error to be nil. Which is not true. The ‘problem’ is in the defer statement, actually. Defer behavior, as described in the Go Blog post:

  1. A deferred function’s arguments are evaluated when the defer statement is evaluated.
  2. Deferred function calls are executed in Last In First Out order after the surrounding function returns.
  3. Deferred functions may read and assign to the returning function’s named return values.

In our case, we have to use a named return value, to actually return err value from the function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func executor() (err error) {
	fmt.Println("in executor")
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()
	job()

	return err
}

 

Even in panic, one needs to keep a good posture ;).

References:

Mon Dec 25, 2023 / 254 words / Golang Panic