GO nil detection [interface, slice, pointer, struct, map]


Written By - Tuan Nguyen
Advertisement

In the previous post, I already talked about the zero values and nil in Golang. In today's article, we will discuss how to detect nil value in Go.nil is a predefined identifier in Go that represents zero values of many types. nil is usually mistaken as null (or NULL) in other languages, but they are different.

 

Using nil in Golang

We can use nil without declaring it. nil can represent the zero values of many data types:

  • pointer types (including type-unsafe ones).
  • map types.
  • slice types.
  • function types.
  • channel types.
  • interface types.

 

Common nil detection in Golang

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

In the example below, we will try to convert a string to unit8 and see how we can catch the error if it is not nil:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	s := "111a"
	fmt.Printf("%v %T\n", s, s)
	in, err := strconv.Atoi(s)
	// catch the error if it is not nil
	if err != nil {
		fmt.Println(err) //strconv.Atoi: parsing "111a": invalid syntax
	}
	fmt.Printf("%v %T\n", in, in) // 0 int

	s2 := "111"
	fmt.Printf("%v %T\n", s2, s2)
	in2, err := strconv.Atoi(s2)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%v %T\n", in2, in2) // 0 int
}

Output:

111a string
strconv.Atoi: parsing "111a": invalid syntax
0 int
111 string
111 int

Explanation

In the code shown below, we try to handle errors by creating an error variable which is an error type. The zero value for the error type is nil, so we can use the operator != to compare our variable to nil. If it is not nil, it means our program has some problems, we will print the error message and try to handle this case.

GO nil detection [interface, slice, pointer, struct, map]

 

Check if a pointer is nil or not

In this example, the pointer is checked whether it is a nil pointer or not:

Advertisement
package main

import "fmt"

type Test struct {
}

func main() {
	var ptr *Test // pointer
	var intVal = 123
	var ptr1 *int = &intVal

	fmt.Printf("ptr is a nil pointer?: %v\n", ptr == nil)
	fmt.Printf("ptr1 is a nil pointer?: %v\n", ptr1 == nil)
}

Output:

ptr is a nil pointer?: true
ptr1 is a nil pointer?: false

 

Check if a struct is nil or not

In this example, we will try to compare a struct instance to nil.

package main

import "fmt"

type Config struct {
	Host     string
	Port     float64
	Username string
	Password string
	Setup    bool
}

func main() {
	var conf Config
	if conf == nil {
		fmt.Println("The config is nil!")
	} else {
		fmt.Println("The config is not nil")
	}
}

Explanation

You are comparing a structure instance and nil, which is why the compiler prompts an error message. Since they are not the same type, so the comparison is invalid. The elements of an array or struct will have their fields zeroed if no value is specified. So there's no way to set a struct value to nil. But you could set the value of a pointer to a struct to nil. As mentioned above, the zero value of pointer types is nil.

package main

import "fmt"

type Config struct {
	Host     string
	Port     float64
	Username string
	Password string
	Setup    bool
}

func main() {
	var conf *Config // nil

	// not nil
	conf2 := &Config{
		Host:     "golinuxcloud.com",
		Port:     22,
		Username: "testuser",
		Password: "pwd",
	}

	if conf == nil {
		fmt.Println("The config is nil!")
	} else {
		fmt.Println("The config is not nil")
	}

	if conf2 == nil {
		fmt.Println("The config is nil!")
	} else {
		fmt.Println("The config is not nil")
	}
}

Output:

The config is nil!
The config is not nil

 

Check if an interface is nil or not

In this example, the interface is checked whether it is a nil interface or not.

package main

import (
	"fmt"
)

type TestInterface interface {
	Summary() string
}

type MyString struct {
	Message string
}

// Implementing methods of
func (myStr MyString) Summary() string {
	return "This is a test message" + myStr.Message
}

func main() {
	var interf TestInterface
	fmt.Printf("interf is a nil interface: %v\n", interf == nil)

	var interf2 TestInterface
	interf2 = MyString{"from GoLinuxCloud"}
	fmt.Printf("interf2 is a nil interface: %v\n", interf2 == nil)
}

Output:

interf is a nil interface: true
interf2 is a nil interface: false

 

Check if a slice is nil or not

In this example, we are going to check if a slice is nil or not. For the slice, you must clearly understand about an empty and a nil slice. nil and empty slices (with 0 capacity) are not the same, but their observable behavior is the same (almost all the time):

  • We can call the builtin len() and cap() functions for both nil and empty slice
  • We can iterate through them with for range (will be 0 iterations)
package main

import "fmt"

func main() {
	var slice1 []int         // nil slice
	slice2 := []int{}        // non-nil, empty slice
	slice3 := make([]int, 0) // non-nil, empty slice
	fmt.Println("slice1", len(slice1), slice1 == nil, slice1[:], slice1[:] == nil)
	fmt.Println("slices2", len(slice2), slice2 == nil, slice2[:], slice2[:] == nil)
	fmt.Println("slice3", len(slice3), slice3 == nil, slice3[:], slice3[:] == nil)
}

Output:

slice1 0 true [] true
slice2 0 false [] false
slice3 0 false [] false

 

Check if a map is nil or not

The following example shows how to check if a map is empty using the length check and check if the map is nil or not:

package main

import "fmt"

func main() {
	tempMap := make(map[string]string) // empty map
	var tempMap2 map[string]string     // nil map
	if len(tempMap) == 0 {
		fmt.Println("tempMap is empty")
	} else {
		fmt.Println("tempMap is not empty")
	}

	if tempMap2 == nil {
		fmt.Println("tempMap2 is nil")
	} else {
		fmt.Println("tempMap2 is not nil")
	}
}

Output:

tempMap is empty
tempMap2 is nil

 

Summary

As you can see, nil is not the zero value for every type but only for pointers, functions, interfaces, slices, channels, and maps. For nil detection, we simply use the comparator !=. Besides that, some packages provide the IsZero() function to check if it is nil or not. For example, the time.Time type has the IsZero() which reports whether the variable represents the zero time instant, January 1, year 1, 00:00:00 UTC.

 

References

https://go.dev/ref/spec#The_zero_value
https://go.dev/doc/tutorial/handle-errors
https://pkg.go.dev/errors

 

Categories GO

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment