A Go Puzzle: Returning without return

Here is a very simple Go program:

package main

import "fmt"

func f() int {
	return 42
}

func main() {
	fmt.Println(f())
}

As expected, it simply prints “42” on the standard output.

The challenge

Now, I have a challenge for you: rewrite the function f so that:

In other words, you must not touch the main function. The program’s behavior is unchanged. It still compiles correctly, runs and promptly prints out “42”. There just isn’t any return in f (or anywhere else!) anymore.

Give it a Go!

Maybe you know about named results and thought “easy, I juste have to write something like that:”

func f() (res int) {
    res = 42
}

Alas, no. Compiler doesn’t agree: “missing return”.

You would still need at least a bare return:

func f() (res int) {
    res = 42
    return
}

But we don’t want no stinky return!

I’ll write a couple hints below and the answer at the end. But you should try solving the puzzle first! Click here to open the Go playground and give it a go!

BTW, there aren’t any tricks in the wording of the problem. You’re truly looking for a way to write a function that returns a result without any return. That’s it.

It doesn’t depend on a compiler bug or a particular implementation or having to use CGO or manipulate the stack or whatever dirty tricks.

It really is 100% pure, proper Go, that should work with any Go version and environment.

(Hints below…)

.

.

.

.

.

.

.

.

.

Hints

Use the spec, Luke!

As said above, this is proper Go. So it’s allowed by the language. So it’s in the spec!

Take a look at these two very similar functions. One actually compiles, and one doesn’t:

func compilesOK() {
    // no body, no return, no nothing, still compiles!
}
func doesntCompile() int {
    // compiler says: missing return
}

Can you find the reason why in the spec?

.

.

.

.

.

.

.

.

.

Terminating statements

The answer to the question above is in the Function declarations section:

If the function’s signature declares result parameters, the function body’s statement list must end in a terminating statement.

doesntCompile declares an int result parameter, so its body must end in a terminating statement. There is no such requirement for compilesOK, which explains the difference.

Terminating statements are listed in the aptly-named Terminating statements section.

You can see that there is the obvious return. But also others!

A surprising one is an infinite for loop. So this function compiles!

func compilesOK() int {
    for {}
}

… but of course, an infinite loop never returns!

Can you find a terminating statement that makes the function actually terminate? Other than return of course!

.

.

.

.

.

.

.

.

.

Panic

panic is a built-in function that terminates the execution of a function. It is also a terminating statement!

So this function compiles!

func compilesOK() int {
    panic(42)
}

But it doesn’t return 42, it makes the function panic. If you called that function in the main’s fmt.Println(), it would just make the whole program terminate with a panic, not print “42” as we want.

Can you find a way to stop the function from actually panicking?

(this was the last hint! Solution below)

.

.

.

.

.

.

.

.

.

The solution

By combining the powers of panic, recover and named results, we can write this function that returns without a return:

func f() (res int) {
	defer func() { recover() }()
	res = 42
	panic("whatever")
}

Proof here :)