Have you ever encountered a situation where you check for nil, but the function still executes unexpectedly? You're not alone!
This is a common pitfall in Go when working with interfaces and nil values. Let's break it down. 👇
🔍 The Problem: A Failing Nil Check
Imagine we have an interface Notifier with a method Notify().
package main
import "fmt"
// Define an interface
type Notifier interface {
Notify()
}
// Define a struct
type Email struct {
Address string
}
// Implement the Notify method for Email
func (e *Email) Notify() {
fmt.Println("Sending email to:", e.Address)
}
// Function that processes Notifier entities
func ProcessNotifiers(notifiers ...Notifier) {
for _, notifier := range notifiers {
if notifier == nil {
fmt.Println("Skipping nil notifier")
continue
}
fmt.Println("Processing:", notifier)
notifier.Notify()
}
}
func main() {
var email *Email // Declaring a nil pointer of type *Email
var notifier Notifier = email // Assigning it to an interface
fmt.Println(notifier == nil) // ❌ False! (unexpected)
ProcessNotifiers(notifier) // Will still call Notify() and panic!
}
🤔 What's Happening Here?
-
1️⃣ email is a nil pointer, but notifier is NOT nil!
- When assigning email to notifier, Go stores its type info (*Email) in the interface.
- This means the interface itself is not nil, even though the underlying value is nil.
-
2️⃣ Our if notifier == nil check fails!
- Even though email is nil, notifier still holds a valid interface value, so notifier == nil returns false.
-
3️⃣ Calling notifier.Notify() causes a panic!
- The method is called on a nil receiver, leading to a runtime error.
✅ The Fix: Proper Nil Checking
Instead of checking if notifier == nil, use reflection to properly detect nil interfaces:
import "reflect"
// Properly check if an interface is nil
func isNil(i interface{}) bool {
if i == nil {
return true
}
v := reflect.ValueOf(i)
return v.Kind() == reflect.Ptr && v.IsNil()
}
func ProcessNotifiersFixed(notifiers ...Notifier) {
for _, notifier := range notifiers {
if isNil(notifier) {
fmt.Println("Skipping nil notifier")
continue
}
fmt.Println("Processing:", notifier)
notifier.Notify()
}
}
✅ Now, it properly skips the nil notifier and avoids the panic!
🚀 Takeaways
✔ Interfaces holding nil pointers are NOT nil!
✔ Always check for nil pointers inside interfaces properly
✔ Use reflection to avoid hidden nil bugs
Have you run into this before? Let's discuss in the comments! 💬👇
Top comments (0)