Using named return variables to capture panics in Go

This is going to be a short post inspired by Sean Kelly’s tweet in November.

The goal is to just document and illustrate a situation where named return variables are necessary, so with that said let’s just jump right in.

Imagine you are writing some code that uses a function that can panic, and for whatever reason (3rd party lib, backwards compatibility, etc) you can’t change that function.

func pressButton() {
  fmt.Println("I'm Mr. Meeseeks, look at me!!")
  // other stuff then happens, but if Jerry asks to
  // remove 2 strokes from his golf game...
  panic("It's gettin' weird!")
}

You still need to use that function, but if it panics you want to capture the panic and return it as an error so you write some code like this:

func doStuff() error {
  var err error
  // If there is a panic we need to recover in a deferred func
  defer func() {
    if r := recover(); r != nil {
      err = errors.New("the meeseeks went crazy!")
    }
  }()

  pressButton()
  return err
}

Then you go run your code and… what’s this? Your error is nil even when the code panics? That’s not what we wanted!

Why does this happen?

While at first it looks like our code is returning the var err error that we create at the start of our function, the truth is our program never gets to that line of code. This means it never actually returns that specific err variable, and altering it inside of our deferred function ends up being pointless.

Adding a Println after the call to pressButton but before the return really helps illustrate this:

pressButton()
// Nothing below here gets executed!
fmt.Println("we got here!")
return err

How do we fix it?

To fix this issue, we can simply use a named return variable.

func doStuff() (err error) {
	// If there is a panic we need to recover in a deferred func
	defer func() {
		if r := recover(); r != nil {
			err = errors.New("the meeseeks went crazy!")
		}
	}()

	pressButton()
	return err
}

The resulting code looks very similar, but by naming our return variable when we declare the function our program will now return the err variable even if we never hit the return statement at the end of our doStuff function. Because of this minor difference, we can now alter the err variable inside of our deferred function and successfully capture the panic.

Learn Web Development with Go!

Sign up for my mailing list and I'll send you a FREE sample from my course - Web Development with Go. The sample includes three chapters from the book, and over 2.5 hours of screencasts.

You will also receive notifications when I release new articles, along with other freebies that I only share with my mailing list.

Avatar of Jon Calhoun
Written by
Jon Calhoun

Jon Calhoun is a full stack web developer who also teaches about Go, web development, algorithms, and anything programming related. He also consults for other companies who have development needs. (If you need some development work done, get in touch!)

Jon is a co-founder of EasyPost, a shipping API that many fortune 500 companies use to power their shipping infrastructure, and prior to founding EasyPost he worked at google as a software engineer.

Related articles

Spread the word

Did you find this page helpful? Let others know about it!

Vote on Hacker News

Sharing helps me continue to create both free and premium Go resources.

Want to discuss the article?

See something that is wrong, think this article could be improved, or just want to say thanks? I'd love to hear what you have to say!

You can reach me via email or via twitter.

Recent Articles Mini-Series Tags About Me Go Courses

©2018 Jonathan Calhoun. All rights reserved.