Prev Next

Golang / GoLang Interfaces and Object Oriented Interview Questions

Explain the nil interface trap in Go. Why does a typed nil fail the '!= nil' check?

This is one of the most frequently asked Go interview questions. The trap: an interface value is only nil when both its type pointer and its data pointer are zero. If you assign a nil pointer of a concrete type to an interface variable, the type pointer becomes non-zero — so the interface is not nil, even though the data it holds is nil.

type MyError struct{ code int }
func (e *MyError) Error() string { return fmt.Sprintf("error %d", e.code) }

// BUGGY function — looks like it returns nil on the happy path
func riskyOperation(fail bool) error {
    var err *MyError   // nil *MyError
    if fail {
        err = &MyError{code: 42}
    }
    return err  // WRONG: assigns typed nil to error interface
                // interface layout: {type=*MyError, data=nil}
}

e := riskyOperation(false)
if e != nil {  // TRUE — interface is non-nil even though data is nil!
    fmt.Println("BUG: this branch runs unexpectedly:", e)
}

// Correct fix: return untyped nil
func safeOperation(fail bool) error {
    if fail {
        return &MyError{code: 42}
    }
    return nil  // untyped nil → both words zeroed → truly nil interface
}

// Another correct approach: keep return type concrete
func concreteReturn(fail bool) *MyError {
    if fail { return &MyError{code: 42} }
    return nil  // nil pointer returned as concrete type, not interface
}

// Debug a suspicious interface with reflect
e2 := riskyOperation(false)
fmt.Println(e2 == nil)                           // false
fmt.Println(reflect.ValueOf(e2).IsNil())         // true — data IS nil
fmt.Printf("%T\n", e2)                           // *main.MyError

Root cause: the language spec defines interface equality as requiring both words to be zero. Assigning any concrete type (even a nil pointer of that type) populates the type word. The only way to produce a nil interface is to assign the bare nil literal or another nil interface variable.

Rule: when your function returns an interface type (like error), always return the bare nil keyword on the success path. Never return a typed nil pointer (var e *MyError; return e).

Why does 'if err != nil' evaluate to true when a nil *MyError pointer is assigned to the error interface?
What is the correct way to return 'no error' from a function with return type 'error'?

Invest now in Acorns!!! 🚀 Join Acorns and get your $5 bonus!

Invest now in Acorns!!! 🚀
Join Acorns and get your $5 bonus!

Earn passively and while sleeping

Acorns is a micro-investing app that automatically invests your "spare change" from daily purchases into diversified, expert-built portfolios of ETFs. It is designed for beginners, allowing you to start investing with as little as $5. The service automates saving and investing. Disclosure: I may receive a referral bonus.

Invest now!!! Get Free equity stock (US, UK only)!

Use Robinhood app to invest in stocks. It is safe and secure. Use the Referral link to claim your free stock when you sign up!.

The Robinhood app makes it easy to trade stocks, crypto and more.


Webull! Receive free stock by signing up using the link: Webull signup.

More Related questions...

What is an interface in Go and how does it differ from interfaces in Java or C#? What is the internal two-word structure of a Go interface value? Explain the nil interface trap in Go. Why does a typed nil fail the '!= nil' check? How does Go's implicit interface satisfaction enable duck typing and loose coupling? What is the empty interface (any) in Go and what are its trade-offs? What is the difference between value receivers and pointer receivers in Go methods? How does interface embedding (composition) work in Go? How does Go achieve runtime polymorphism using interfaces? How does Go implement code reuse without inheritance? Explain composition via struct embedding. How does embedding help a struct satisfy an interface? How is the built-in error type defined and how do you implement custom errors? What is the fmt.Stringer interface and how do you implement it? How do type assertions work on interface values and when do you use them? Explain the Go design principle: 'Accept interfaces, return concrete types'. How do Go interfaces enable dependency injection and improve testability? How do you sort custom types using sort.Interface in Go? How do io.Reader and io.Writer demonstrate Go's interface design philosophy? Why can't you always use a value of type T where *T is needed for interface satisfaction? How can a single Go type implement multiple interfaces simultaneously? How does Go implement encapsulation without private/public class modifiers? How do the fmt.Stringer and error interfaces work together and how do you avoid infinite recursion? How do generics relate to interfaces in Go 1.18+ and what are type constraints? How do you implement an abstract type pattern in Go using interfaces and constructors? What is the compile-time interface check idiom and why is it important? How does equality work for interface values in Go? How does the io.Closer interface work with defer for resource management? How does Go differ from classical OOP in terms of inheritance and method overriding? How does http.Handler demonstrate real-world interface design in Go? What is the function-as-interface pattern in Go and how does it enable flexible APIs? What is interface pollution and how do you avoid it in Go? What is the difference between fmt.Stringer and fmt.GoStringer? How does Go's standard library use interface layering for I/O transformation? When should you use reflection instead of interfaces for type-agnostic code in Go? How does thinking about interfaces as 'behaviours, not data' guide better design? What is the interface upgrade (optional interface) pattern in Go? What is the difference between embedding a struct and embedding an interface inside a struct? Are interface values in Go safe to use concurrently? How do you combine table-driven tests with interface mocks in Go? How is context.Context an interface and how does its design demonstrate Go best practices? How does Go embody the Interface Segregation Principle (ISP)? How do type aliases and type definitions differ in relation to interface satisfaction in Go? How does embedding propagate interface satisfaction through multiple levels? What is the zero value of an interface type and how does it differ from a zero-value struct? How do you use wrapper types to adapt existing types to satisfy interfaces in Go? Walk through a complete Go OOP design: payment processing without inheritance. What are the edge cases in Go interface value equality that trip up experienced developers? What is sync.Locker and how is it used in real-world Go concurrency code? Summarise the key rules and best practices for Go interfaces that interviewers test.
Show more question and Answers...

GoLang Concurrency Mastery Interview Questions

Comments & Discussions