This article was original posted on my website at calhoun.io
This is going to be a short post inspired by Sean Kelly's tweet a while back.
Liquid error: internal
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
}
Run it on the Go Playground: https://play.golang.org/p/wzkjKGqFPL
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
Run it on the Go Playground: https://play.golang.org/p/Vk0DYs20eB
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
}
Run it on the Go Playground: https://play.golang.org/p/bqGOroPjQJ
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.
Top comments (3)
You can even remove the variable name when using named variable returns.
Just
return
will suffice. Go will automatically return theerr
variable from within the function block.I know but I prefer the more explicit version
Ok then.