To panic, or not to panic

Time for the very first Go Puzzlers challenge. Today’s riddle: Can you call methods on a nil value in Go—and not crash like in Java or PHP?

The Puzzle

What does this program print?

package main

import "fmt"

type User struct {
	Name string
}

func (u *User) Greeting() string {
	if u == nil {
		return "Hello, mysterious guest!"
	}
	return "Hello, " + u.Name
}

func (u *User) NameLen() int {
	if u == nil {
		return 0
	}
	return len(u.Name)
}

func main() {
	u := &User{Name: "Gopher"}
	fmt.Println(u.Greeting(), u.NameLen())

	u = nil
	fmt.Println(u.Greeting(), u.NameLen())
}

Think before you run it. Does the second pair of calls panic, or…?

The answer

Show answer

It prints:

Hello, Gopher 7
Hello, mysterious guest! 0

No panic!

Why this works?

  • In Go, methods with pointer receivers (like func (u *User) ...) can be invoked even when the receiver is nil.
    Under the hood it’s just a function call with a *User parameter whose value happens to be nil.
  • No automatic dereference happens inside your methodyou control whether you dereference.
    As long as the method checks if u == nil (and avoids u.Name before that), it can return a sensible result.
  • This is different from languages like Java/PHP, where calling a method on null explodes immediately.

Gotchas & bonus round

  1. Value receiver vs pointer receiver
    If you write a method with a value receiver, e.g.:

    func (u User) Initial() byte { return u.Name[0] }

    and then do:

    var u *User // nil
    fmt.Println(u.Initial()) // 💥


    this will panic. Calling a value-receiver method on a *User requires the compiler to dereference u to a concrete User value—dereferencing nil panics.
  2. Interfaces can still bite
    Calling a method on a nil interface value will panic because there’s no dynamic type to dispatch to.
    (var g Greeter; g.Greet() → 💥)

Takeaways

  • You can design methods on pointer receivers that treat nil as a meaningful “zero value” (e.g., a guest user).
  • Be deliberate: check for nil at the top of such methods, and avoid dereferencing until you’ve handled it.
  • Prefer pointer receivers for types where a “zero value” makes sense and methods can be no-ops or safe defaults.

Got a neat twist on this puzzle? Drop it in the comments—let’s learn Go by solving.

Leave a Reply

Your email address will not be published. Required fields are marked *