DEV Community

Moksh
Moksh

Posted on

🚀 Go Interface Nil Pitfall: Why Your Nil Check is Failing (and How to Fix It!) 🔍

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!
}
Enter fullscreen mode Exit fullscreen mode

🤔 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()
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ 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)