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 7Hello, 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 isnil.
Under the hood it’s just a function call with a*Userparameter whose value happens to benil. - No automatic dereference happens inside your method—you control whether you dereference.
As long as the method checksif u == nil(and avoidsu.Namebefore that), it can return a sensible result. - This is different from languages like Java/PHP, where calling a method on
nullexplodes immediately.
Gotchas & bonus round
- 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*Userrequires the compiler to dereferenceuto a concreteUservalue—dereferencingnilpanics. - 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
nilas a meaningful “zero value” (e.g., a guest user). - Be deliberate: check for
nilat 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