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! 💬👇

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay