{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/go/nil-interface-comparison/",
  "description": "Debug tricky nil comparisons in Go interfaces. Understand dynamic types, type assertions, and use reflect for generic nil checking.",
  "path": "/go/nil-interface-comparison/",
  "publishedAt": "2025-03-12T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "Go",
    "Data Structures"
  ],
  "textContent": "Comparing interface values in Go has caught me off guard a few times, especially with nils.\nOften, I'd expect a comparison to evaluate to true but got false instead.\n\nMany moons ago, Russ Cox wrote a fantastic [post on Go interface internals] that clarified\nmy confusion. This post is a distillation of my exploration of interfaces and nil\ncomparisons.\n\nInterface internals\n\nRoughly speaking, an interface in Go has three components:\n\n- A static type\n- A dynamic type\n- A dynamic value\n\nFor example:\n\nHere, the static type of n is any, which tells the compiler what operations are allowed\non the variable. In the case of any, any operation is allowed. When we assign 1 to n,\nit adopts the dynamic type int and the dynamic value 1.\n\nInternally, every interface value is implemented as a two [word] structure:\n\n- One word holds a pointer to the dynamic type (i.e., a type descriptor).\n- The other word holds the data associated with that type.\n\nThis data word might directly contain the value if it's small enough, or it might hold a\npointer to the actual data. Note that this internal representation is distinct from the\ninterface's declared or \"static\" type - the type you wrote in the code (any in the example\nabove). At runtime, what gets stored is only the pair of dynamic type and dynamic value.\nHere's a crude diagram:\n\nComparing nils with interface variables\n\nNil comparisons can be tricky because an interface value is considered nil only when both\nits dynamic type and dynamic value are nil. A few examples.\n\nComparing a nil pointer directly\n\nHere, p is a pointer to an int and is explicitly nil, so the comparison works as expected.\nThis doesn't have anything to do with explicit interfaces, but it's important to demo basic\nnil comparison to understand how comparisons work with interfaces.\n\nAn interface variable explicitly set to nil\n\nIn this case, r is directly set to nil. Since both the dynamic type and the dynamic value\nare nil, the interface compares equal to nil.\n\nAssigning a nil pointer to an interface variable\n\nEven though b is nil, assigning it to the interface variable r gives r a non-nil\ndynamic type (bytes.Buffer) with a nil dynamic value. Since r still holds type\ninformation, r == nil returns false, even though the underlying value is nil.\n\n> When comparing an interface variable, Go checks both the dynamic type and the value. The\n> variable evaluates to nil only if both are nil.\n\nUsing type assertions for reliable nil checks\n\nIn cases where an interface variable might hold a nil pointer, we've seen that comparing the\ninterface directly to nil may not yield the expected result.\n\nA type assertion can help extract the underlying value so that you can perform a more\nreliable nil check. This approach is especially useful when you know the expected underlying\ntype.\n\nBelow, we define a simple type myReader that implements the Read method to satisfy the\nio.Reader interface.\n\nNow, consider the following example:\n\nHere, we assert that r holds a value of type myReader. If the assertion succeeds\n(indicated by ok being true) and the underlying value is nil, we can conclude that\nthe interface variable holds a nil pointer - even though the interface itself is not nil due\nto its dynamic type.\n\nThis type assertion trick only works when you know the underlying type of the interface\nvalue. If the type might vary, consider using the reflect package to examine the underlying\nvalue.\n\nWriting a generic nil checker with reflect\n\nThe following function introspects any variable and checks whether it's nil:\n\nThe switch on .Kind() is necessary because directly calling reflect.ValueOf().IsNil() on\na non-pointer value will cause a panic.\n\nCalling this function on any value, including an interface, reliably checks whether it's\nnil.\n\nFin!\n\n\n\n\n[post on Go interface internals]:\n    https://research.swtch.com/interfaces\n\n[word]:\n    https://en.wikipedia.org/wiki/Word_(computer_architecture)",
  "title": "Nil comparisons and Go interface"
}