Tricky Golang interview questions - Part 5: interface == nil
Tags: go, interview, tutorial, programming
Published: 2024-06-26 14:09:30

Another topic I would like to discuss in these series is the interface to nil comparison problem. This question is frequently asked during many golang interviews and requires an understanding of how types are constructed under the hood.

**Question: Which `if` statements will be evaluated as `true`?**
```go
package main  
  
import "fmt"  
  
type SomeType interface {  
    Get()  
}  
  
type SomeImpl struct {...}  
func (i SomeImpl) Get() {...}  
  
func main() {  
    var aType SomeType  
    if aType == nil {
       fmt.Println("nil interface")  
    }  
  
    var aImpl *SomeImpl  
    if aImpl == nil {
       fmt.Println("nil struct")  
    }  
  
    aType = aImpl  
    if aType == nil {
       fmt.Println("nil assignment")  
    }  
}
```
As we all know (hope so) the empty interface in golang holds a `nil` value. The default value of an empty interface is **nil**. This means the uninitialised variable `var aType SomeType` holds a `nil` value because it has a type of empty (nil) interface. In this case, the if condition is fulfilled, we will see a print statement in the terminal:
```go
[Running] go run "main.go"

nil interface
```

Good, let's continue further. 
We have another uninitialised variable `var aImpl *SomeImpl` which is a pointer to the struct. As you know in golang **all memory is initialised (zeroed)** this means that the pointers, even if they are uninitialised, will have a default value, which is `nil`. So we have one more fulfilled if condition:
```go
[Running] go run "main.go"

nil interface
nil struct
```

For the last, we see a value assignment (value initialisation) of a struct pointer value to the interface variable `aType = aImpl`. Judging by the earlier made statements it is logical to assume that we assign a `nil` value to the `var aType` and in the result, `aType` will remain `nil`. 
```go
[Running] go run "main.go"

nil interface
nil struct
nil assignment

[Done] exited with code=0 in 0.318 seconds
```

This sounds logical, so the confident answer is:
**The program will output all print statements into the terminal because all if statements will be fulfilled during the execution!**

Okay, that sounds good (would say the interviewer). Let's run the program and check the results. The actual result looks like this:
```go
[Running] go run "main.go"

nil interface
nil struct

[Done] exited with code=0 in 0.318 seconds
```

As you already noticed, the output does not contain the last `nil assignment` print statement. So, why this happened?
To answer this question we must dig deeper into the language to understand how interfaces are constructed in golang.

#### Interface
In golang, an interface is a type that specifies a set of method signatures. When a value is assigned to an interface, golang constructs an interface value that consists of two parts: **the dynamic type and the dynamic value**. This is commonly referred to as the “interface tuple.”

 1. **Dynamic Type**: This is a pointer to a type descriptor that describes the type of the concrete value stored in the interface.
 2. **Dynamic Value**: This is a pointer to the actual value that the interface holds.

The interface tuple can be represented as the following structs:
```go
type iface struct {
    tab  *itab
    data unsafe.Pointer
}

type itab struct {
    inter *interfacetype
    _type *_type
    hash  uint32
    _     [4]byte
    fun   [1]uintptr  // variable sized, actually [n]uintptr
}
```
- `tab`: A pointer to an `itab` structure that contains information about the type and the methods that the type implements for the interface.
- `data`: A pointer to the actual data held by the interface.

When a value is assigned to an interface, golang finds the type descriptor for the concrete type being assigned to the interface. Then sets up the method table (`itab`) that allows method calls through the interface to be dispatched to the correct implementation and finally stores a pointer to the actual value in the data field of the interface.

When `aType = aImpl` is executed 
1. **Determining Interface Implementation**: golang first determines that `*SomeImpl` (a pointer to `SomeImpl`) implements the `SomeType` interface because `*SomeImpl` has a method `Get()` with the correct signature.
2. **Looking Up the Type Descriptor**: golang looks up the type descriptor for `*SomeImpl`.
3. **Creating the** **`itab`**: golang creates an `itab` structure
5. **Assigning the Pointer**: golang assigns the pointer to the `SomeImpl` value to the data field of the interface.

```
interface (aType)
+------------+           +-----------+
| tab        |---------->|  itab     |
|            |           |-----------|
| data       |--+        |  inter    |
+------------+  |        |  _type    |
                |        |  fun[0]   |
                |        +-----------+
                |
                v
         +---------------+
         |   *SomeImpl   |
         +---------------+
         |   ........    |
         +---------------+
```

#### Summary
To sum up, what we learned, the previous explanation in short means:
``` 
+ uninitialised +-------------+ initialised +

interface (aType)             interface (aType)
+------------+                +--------------------------------+
| tab:  nil  |                | tab:  type of *SomeImpl        |
| data: nil  |                | data: value of *SomeImpl (nil) |
+------------+                +--------------------------------+
```

Now the tricky part. 
In golang when checking if an `interface` is `nil`, both the `tab` and `data` fields must be `nil`. **If an `interface` holds a `nil` pointer of a concrete type, the `tab` field will not be `nil`, so the `interface` itself will not be considered `nil`.**

This is the reason why we don't see the last print statement inside the terminal. After execution of `aType = aImpl` the variable `aType` is no longer considered as a nil / empty interface.

It's that easy!