When nil Isn't Equal to nil
Tags: go, beginners, nil
Published: 2018-09-24 21:10:11

---
title: When nil Isn't Equal to nil
published: true
description: It is easy to get confused by when and why different variants of nil will be equal and when they won't be in Go. In this article we explore why this happens so that you know what to expect when writing your code.
tags: golang, go, beginner, nil
canonical_url: https://www.calhoun.io/when-nil-isnt-equal-to-nil/
---

> *This article was original posted on my website at [calhoun.io](https://www.calhoun.io/when-nil-isnt-equal-to-nil/) and stems from [a question asked on the Go Forums](https://forum.golangbridge.org/t/a-nil-a-b-b-nil-with-pointers-and-interface/10593).*

In this article we are going to explore a few situations where variables that appear to be equal to the developer will evaluate to false when we compare them using Go's `==` operation. We will also discuss why it happens, which will hopefully make it easier to avoid running into this problem in your own code.

First let's look at an example of what I mean. Imagine we had two variables, each with their own type, but each are assigned the hard-coded value of `nil`.

```go
var a *int = nil
var b interface{} = nil
```

What do you expect the following evaluations to be?

```go
fmt.Println("a == nil:", a == nil)
fmt.Println("b == nil:", b == nil)
fmt.Println("a == b:", a == b)
```

*Run it on the Go Playground: <https://play.golang.org/p/2I6FQ_j5gFc>*

If you want to run the code on the Go playground to verify you can, but here is what the actual output will be:

```
a == nil: true
b == nil: true
a == b: false
```

Now let's quickly look at another example that is very similar, but slightly different. We are going to change the initial value of `b`.

```go
var a *int = nil
// This is the only change - we assign b to a instead
// of the hard-coded nil value.
var b interface{} = a

fmt.Println("a == nil:", a == nil)
fmt.Println("b == nil:", b == nil)
fmt.Println("a == b:", a == b)
```

*Run it on the Go Playground: <https://play.golang.org/p/Tj-ImVLqMtx>*

Again, what do you expect the output to be? Below is the updated output to check your answer.

```
a == nil: true
b == nil: false
a == b: true
```

## What in the world is going on?

This phenomena is somewhat hard/weird to explain, but no, it is not a bug in the language, and no, it isn't random/magical/whatever else. There are some clear rules being applied, we just need to take some time to understand them. After that this will all make sense and you will understand why you occasionally see people write code like this:

```go
if a == nil {
  b = nil
}
```

Instead of just assigning `b` to `a`.

The first thing we need to understand is that every pointer in Go has two basic pieces of information; the type of the pointer, and the value it points to. We will represent these as a pair like `(type, value)` moving forward.

The fact that every pointer variable needs a type is why we can't have a nil value assigned to a variable without declaring the type as well. That is, the following code will NOT compile.

```go
// This does not work because we do not know the type
n := nil
```

In order to make this code compile we must use a typed pointer and assign it to the value nil:

```go
var a *int = nil
var b interface{} = nil
```

Now both of these variables have types. We can even use the `fmt.Printf` function to print out those types and see what they are.

```go
var a *int = nil
var b interface{} = nil

fmt.Printf("a=(%T, ...)\n", a)
fmt.Printf("b=(%T, ...)\n", b)
```

*Run it on the Go Playground: <https://play.golang.org/p/wCdObTPinJj>*

**Note**: *`%T` is used to print out a value's type with `fmt.Printf`. You can read more about these special characters [in the docs](https://golang.org/pkg/fmt/#hdr-Printing).*


The output to this code is shown below.

```
a=(*int, ...)
b=(<nil>, ...)
```

It appears that when we assign `nil`, the hard-coded value, to the `*int` variable `a` the type will be set to the same type used when defining the `a` variable - `*int`. That makes sense.

The second variable, `b`, is a little more confusing. Its type is `interface{}` (the [empty interface](https://www.calhoun.io/how-do-interfaces-work-in-go/)), but the type given when we print out the nil value is `<nil>`. What is going on?

The short version is that because we use the empty interface, any type will satisfy. The `<nil>` type is technically a type, and it satisfies the empty interface, so it is used when no other type information can be determined by the compiler.

Okay, so we know all pointers have a `(type, value)` associated with them and we have seen what happens when we assign `nil` to each variable. Now let's look at what those types are when we assign `b` to nil using the `a` variable rather than a hard-coded `nil`.

```go
var a *int = nil
var b interface{} = a

fmt.Printf("a=(%T, ...)\n", a)
fmt.Printf("b=(%T, ...)\n", b)
```

*Run it on the Go Playground: <https://play.golang.org/p/j86pv3lnd58>*

Huh... It looks like `b` has a new type.

Before when we assigned `b` to the hard-coded nil value we had no type information. Now that we are assigning `b` to the value `a` that is no longer true. We know exactly what type information to use - whatever was used by the `a` variable!

> **Quick summary so far...**
> - All pointers have both a type and a value they point to.
> - When we assign a variable to the hard-coded `nil` value, the compiler has to determine the correct type to use.
> - When we assign a variable to another nil variable, the type can be determined based on the other variable's type.


## What happens when we check for equality?

Now that we understand how these types are being determined, let's look at what happens when we check for equality in our code.

We will start with both `a` and `b` being assigned to hard-coded `nil` values. After that we will look at a similar snippet where `b` is assigned to the variable `a`.

```go
var a *int = nil
var b interface{} = nil

// We will print out both type and value here
fmt.Printf("a=(%T, %v)\n", a, a)
fmt.Printf("b=(%T, %v)\n", b, b)
fmt.Println()
fmt.Println("a == nil:", a == nil)
fmt.Println("b == nil:", b == nil)
fmt.Println("a == b:", a == b)
```

*Run it on the Go Playground: <https://play.golang.org/p/i8kaKz8qGEh>*

The output to this program is shown below.

```
a=(*int, <nil>)
b=(<nil>, <nil>)

a == nil: true
b == nil: true
a == b: false
```

The obviously weird part here is that `a` does NOT equal `b`. This seems incredibly weird, because at first glance it looks like we are saying that `a == nil` and `b == nil` but `a != b` which is not logically possible.

The reality of the situation is that what we have written - eg `a == nil` - is not a true representation of what is actually being compared.  What we are really comparing is both the values AND the types. That is, we are not just comparing the value stored in `a` with the `nil` value; we are also comparing their types. This is shown more explicitly below.

```bash
a == nil: (*int, <nil>) == (*int*, <nil>)
b == nil: (<nil>, <nil>) == (<nil>, <nil>)
# Notice that these two are clearly not equal
# once we add in the type information.
a == b: (*int, <nil>) == (<nil>, <nil>)
```

When written the comparisons this way it becomes pretty clear that the two are not equal - they have different types - but this is all information not explicitly clear in our code which is what unfortunately leads to this common misunderstanding.


> **An alternative approach**
> If you actually wanted to compare `a` and `b` in your code, you would probably instead want to write something like:
> 
> ```go
> if a == nil && b == nil {
>   // both are nil!
> }
> ```
> 
> This is more code, but is more frequently going to match your intent. That said, this approach can be botched by assigning `b` to another nil variable (rather than the hard-coded `nil`) as we will see in the next example.



Now let's look at what happens when we assign `b` to the `a` variable and perform the same comparisons.

```go
var a *int = nil
var b interface{} = a // <- the change

fmt.Printf("a=(%T, %v)\n", a, a)
fmt.Printf("b=(%T, %v)\n", b, b)
fmt.Println()
fmt.Println("a == nil:", a == nil)
fmt.Println("b == nil:", b == nil)
fmt.Println("a == b:", a == b)
```

*Run it on the Go Playground: <https://play.golang.org/p/dwkTU54PZRr>*

The output of this program is...

```
a=(*int, <nil>)
b=(*int, <nil>)

a == nil: true
b == nil: false
a == b: true
```

Now the odd man out is the second line, `b == nil`.

This one is a little less obvious, but when we compare the variable `b` to a hard-coded `nil` our compiler once again needs to determine what type to give that `nil` value. When this happens the compiler makes the same decision it would make if assigning `nil` to the `b` variable - that is it sets the right hand side of our equation to be `(<nil>, <nil>)` - and if we look at the output for `b` it clearly has a different type: `(*int, <nil>)`.

A common thought at this point is that this is confusing, and the language should just handle this detail for us. Unfortunately that isn't possible at compile-time because the actual type of the `b` variable can change as the program runs.

```go
var a *int = nil
var b interface{} = a
var c *string = nil

fmt.Printf("b=(%T, %v)\n", b, b)
fmt.Println("b == nil:", b == nil)

b = c
fmt.Printf("b=(%T, %v)\n", b, b)
fmt.Println("b == nil:", b == nil)

b = nil
fmt.Printf("b=(%T, %v)\n", b, b)
fmt.Println("b == nil:", b == nil)
```

*Run it on the Go Playground: <https://play.golang.org/p/_or0_qmZ7iv>*

In this program our `b` variable has its type change three times. It starts out as `(*int, <nil>)`, then becomes `(*string, <nil>)`, and finally ends as `(<nil>, <nil>)`.

There is no way for the compiler each of these types at compile-time, which means that this could only be handled automatically in Go if it became a runtime decision, which would have its own set of unique complications that likely aren't worth introducing.


## Compiler type decisions can also be demonstrated with numbers

We saw how `nil` can be coerced into the correct type by the compiler, but this isn't actually the only situation where the compiler makes type decisions like this. For instance, when you assign variables to a hard-coded number the compiler will decide which type should be used based on the context of the program.

The obvious situation is when the type is declared along with the variable (eg `var a int = 12`), but this can also happen when we pass a hard-coded value into a function or when we just assign a variable to a number. All of these situations are shown below.

```go
package main

import "fmt"

func main() {
	var a int = 12
	var b float64 = 12
	var c interface{} = a
	d := 12 // will be an int

	fmt.Printf("a=(%T,%v)\n", a, a)
	fmt.Printf("b=(%T,%v)\n", b, b)
	fmt.Printf("c=(%T,%v)\n", c, c)
	fmt.Printf("d=(%T,%v)\n", d, d)
	useInt(12)
	useFloat(12)
}

func useInt(n int) {
	fmt.Printf("useInt=(%T,%v)\n", n, n)
}

func useFloat(n float64) {
	fmt.Printf("useFloat=(%T,%v)\n", n, n)
}
```

*Run it on the Go Playground: <https://play.golang.org/p/tSoxf4ohY7C>*

We can even demonstrate some of the comparison confusion using numbers.

```go
var a int = 12
var b float64 = 12
var c interface{} = a

fmt.Println("a==12:", a == 12) // true
fmt.Println("b==12:", b == 12) // true
fmt.Println("c==12:", c == 12) // true
fmt.Println("a==c:", a == c) // true
fmt.Println("b==c:", b == c) // false
// We cannot compare a and b because their types
// are clearly never going to match.
```

*Run it on the Go Playground: <https://play.golang.org/p/yNV0c6hAbtX>*

So `a == 12`, `b == 12` and `c == 12`, but if we compare `b == c` we get back false. What?!?!

Again, it goes back to the underlying types:

```go
a=(int,12)
b=(float64,12)
c=(int,12)
```

`a` and `c` have the `int` type. `b` has a `float64` type, so when we compare them with hard coded values like `12` those need coerced into a type before the comparison can happen.

Another interesting note specific to numbers is that when comparing `12` to an interface, the compiler will always coerce it into an `int`. This is similar to how `nil` gets coerced into `(<nil>, <nil>)` when compared to an interface, and we can demonstrate this by changing our last code snippet to instead be:

```go
var b float64 = 12
var c interface{} = b

fmt.Println("c==12:", c == 12)
fmt.Printf("c=(%T,%v)\n", c, c)
fmt.Printf("hard-coded=(%T,%v)\n", 12, 12)
```

*Run it on the Go Playground: <https://play.golang.org/p/9gEsrSO9zTS>*

Which has the following output:

```
c==12: false
c=(float64,12)
hard-coded=(int,12)
```

Now `c == 12` returns false because `(float64, 12)` is not the same as the hard-coded `(int, 12)` because it has a different type.

## In summary...

When we compare hard-coded values with variables the compiler has to assume they have some specific type and follows some set of rules to make this happen. Sometimes this can be confusing, but over time you get used to it.

If you find yourself working with various types that can all be assigned to nil, one common technique for avoiding issue is to explicitly assign things to `nil`. That is, instead of `a = b` write:

```go
var a *int = nil
var b interface{}

if a == nil {
  b = nil
}
```

Now when we later compare `b` to a hard-coded `nil` we will get the results we expected. It is a little more work, but it leads to much better overall results in almost all circumstances.

> **Disclaimer**
> I haven't studied the compiler or the inner-workings of Go in any real capacity, so if any of this is inaccurate someone please let me know and I'll fix it. This is all based on observation and other articles I have read.