I often hear Gophers say that named return parameters in Go are terrible because they enable naked returns, and I 100% agree. Naked returns are possibly Go's worst feature. For a language that prides itself on readability and clarity, returning implicit values seems counterintuitive. In my opinion, it actually adds more cognitive load instead of explicitly returning the value after the return statement.
Despite this, I still used named return parameters for a long time because I focused on what they did add. I felt that they conveniently added valuable context to a function's purpose within the signature. So, before you even entered the function block, it was clear what was being initialized and returned, and I liked that. It's extra documentation without extra documentation. Plus, I knew I'd never utilize a naked return, and I'd definitely flag that in a code review if I ever saw it.
Well, it was a good feature until it wasn't. The other problem with named return parameters is that they're initialized with their zero value. That's not really an issue for, say, a string or a bool, whose zero values are an empty string or false.
However, it does become an issue when you have a named return parameter of type map. In this cases, maps are initialized with their zero value of nil.
For example, this code seems reasonable and will compile without warning, but will cause a runtime error:
package main
func main() {
a := createMap()
fmt.Println(a)
}
func createMap() (myMap map[string]bool) {
myMap["one"] = true
myMap["two"] = true
myMap["three"] = true
return myMap
}
This wouldn't be a big issue if you remembered to initialize the map with either the make keyword (what I'd prefer) or using a composite literal. Unfortunately, if you overlook this step, the compiler will let it slide. In theory, a perfect storm could come along and you don't initialize these types properly, you might miss this in a unit test, and you might not have a linter catch this in CI. This could make its way to production, and the result would be a runtime panic.
panic: assignment to entry in nil map
What this function should look like is:
func createMap() (myMap map[string]bool) {
myMap = make(map[string]bool)
myMap["one"] = true
myMap["two"] = true
myMap["three"] = true
return myMap
}
It's true that the same issue could happen even without named returns. You could forget to initialize the map properly, but my issue with named return parameters is that they can give the illusion of proper initialization and, I'd argue, that they increase the risk of a runtime panic. If you don't use a named return parameter, you have to assign and initialize the returned slice within the function body, which, in my opinion, is easier to remember for both the author and the reviewer of the code. Personally, I think that omitting the named return parameter makes it easier to remember that we are responsible for explicitly initializing the map.
func createMap() map[string]bool {
myMap = make(map[string]bool)
myMap["one"] = true
myMap["two"] = true
myMap["three"] = true
return myMap
}
So, while named return parameters in Go can be useful for adding clarity to a function's signature, they come with the risk of inadvertently returning the zero value (nil) or a runtime error. Always remember to initialize your data structures properly. If naked returns weren't enough to turn you away from this, then maybe this other reason will.
Thanks for coming to my TED Talk.