Return statements

July 25, 2024

Return statements

A “return” statement in a function F terminates the execution of F, and optionally provides one or more result values. Any functions deferred by F are executed before F returns to its caller.

ReturnStmt = "return" [ ExpressionList ] .

The good ol’ return statement. We’ve all seen them. There’s a good chance you think you understand them pretty well. And you might. But there are a few subtleties that we’ll be looking at for the next couple of days.

It should be no surprise that a return statement terminates execution of the function in which it’s found. Although not explicitly stated here, this implies that a return statement needn’t appear at the end of the function. And that’s true. A function may have multiple return statements for different execution paths (or in certain circumstances, zero).

func evenOrOdd(i int) string {
	if i%2 == 0 {
		return "even"
	}
	return "odd"
}

There’s no need to do the more verbose, and in my opinion, harder to read, varation, which is common in some coding styles or languages:

func evenOrOdd(i int) string {
	var result string
	if i%2 == 0 {
		result = "even"
	} else {
		result = "odd"
	}
	return result
}

What’s a bit more profound and important in the above description is the sentence “Any functions deferred by F are executed before F returns to its caller.” This allows you to do any necessary cleanup, regardless of the precise execution path, and in some cases (as we’ll see later), modify return values before they’re returned to the caller.

func evenOrOdd(i int) string {
	t := openTranslationsDatabase()
	defer t.Close()
	if i%2 == 0 {
		return t.GetTranslation("even")
	}
	return t.GetTranslation("odd")
}

In this rather ridiculous example, we open some sort of translation database, but we want to be sure to close it when the function exits. We could call t.Close() before each of the return statements individually, but that’s repetitive, and error prone as we might forget (especially in larger, more complex functions). So we can simply defer t.Close() immediately after opening the resource, and we’re assured it will be closed whenever the function returns.

There is one exception to this, not directly related to return statements, but it’s worth bringing up because it’s topical: If your program exits by way of calling os.Exit, the process terminates immediately, and deferred functions are not executed. This naturally applies equally to other functions that call os.Exit, such as the infamous log.Fatal. (I’ve written before about why log.Fatal and similar functions should be avoided, if you’re interested.)

Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Get daily content like this in your inbox!

Subscribe