Skip to Content
Course content

18: Named Return Values

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a pull request from a junior dev who was building a complex network packet parser. The function was designed to return three different integers: the packet length, the checksum, and the payload size. The signature looked like this: func parsePacket(data []byte) (int, int, int). Within an hour of deployment, we hit a critical bug in production. It turned out the developer had accidentally swapped the order of the checksum and the length in the return statement. Because they were all just int, the compiler didn't blink, and we spent half a day chasing a ghost in the logs.

That's the exact problem named return values solve. Instead of just telling Go what type of data is coming back, you tell it what that data represents right in the function signature.

Self-Documenting Signatures

When you name your return values, you're essentially creating a contract that is visible to anyone reading your code. Instead of guessing which int is which, the signature tells the story. Here is how I would have rewritten that packet parser:

func parsePacket(data []byte) (length, checksum, payloadSize int) {
    // Go automatically initializes these variables to their zero values
    length = len(data)
    checksum = calculateChecksum(data)
    payloadSize = length - 4 
    
    return // This is a "naked" return
}

Notice two things happening here. First, the signature (length, checksum, payloadSize int) defines those variables for the entire scope of the function. You don't need to use := inside the body; you just assign values to them. Second, it makes the code significantly harder to mess up because you're interacting with named variables rather than anonymous slots in a tuple.

The Trade-off of Naked Returns

In the example above, I used a "naked" return—a return statement without any variables listed. Go knows to just grab the current values of the named return variables and send them back. While this feels like a neat shortcut, I'll give you a piece of advice: use it sparingly.

Naked returns are great in short functions (maybe 5-10 lines) where the logic is linear. But in a long, complex function with multiple if/else branches, a naked return becomes a liability. If you're scrolling through 50 lines of code and hit a return, you shouldn't have to scroll all the way back to the top of the function to remember what exactly is being sent back to the caller. In those cases, I still prefer being explicit: return length, checksum, payloadSize.

Essentially, named returns provide two distinct benefits: they document your API for other developers, and they allow you to initialize return variables at the very top of your function logic, which often leads to cleaner code when handling errors.




📋 Practical Task

Refactoring the RGB Color Splitter

You've inherited a piece of code that parses a hex color string (like "#FF5733") into its Red, Green, and Blue components. Currently, the function is confusing because it returns three int values without any names, and the developer is manually managing temporary variables.

Your task: Refactor the splitRGB function to use named return values. Remove the manual variable declarations inside the function body and implement a naked return at the end.

package main

import (
	"fmt"
	"strconv"
)

// Refactor this function
func splitRGB(hex string) (int, int, int) {
	hex = hex[1:] // remove #
	
	r, _ := strconv.ParseInt(hex[0:2], 16, 64)
	g, _ := strconv.ParseInt(hex[2:4], 16, 64)
	b, _ := strconv.ParseInt(hex[4:6], 16, 64)
	
	return int(r), int(g), int(b)
}

func main() {
	r, g, b := splitRGB("#FF5733")
	fmt.Printf("Red: %d, Green: %d, Blue: %d\n", r, g, b)
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.