Anyone who has written Go code is no doubt familiar with the following code:

iferr ! =nil {
    / /...
}
Copy the code

This line of code will be abundant in the Go project, and there may be a catch.

1. Nil in Go

Nil in Go stands for zero value, meaning nothing, and there are similar designs in other languages, such as NULL in Java.

Not all types of zeros are nil. The different types of zeros in Go are as follows:

Data types in Go can be divided into basic types and compound types.

The zeros for basic types are different, some are numbers, some are empty strings, and for compound types, zeros are nil.

A zero can cause a program to crash in some cases, like the zero value of a pointer, because the pointer is pointing to a memory address, and if the pointer is nil, that means it doesn’t point to any address, and then using this pointer memory operation can cause panic.

It is also important to note that nil is not a keyword. Nil is defined in Go like this:

var nil Type // Type must be a pointer, channel, func, interface, map, or slice type
Copy the code

Nil types can only be Pointers, channels, functions, interfaces, and slices.

That is, it is perfectly legal to write code like this:

var nil = make(map[string]string)
Copy the code

But never do that, or the program will die.

2. Traps in nil

As you can see from the definition of nil, nil can only be used with a few types, such as Pointers.

The structure of a pointer is relatively simple, it just points to an address in memory, and we can safely use nil to tell if a pointer is empty, if it points to an address in memory.

Channel, Map, function, and slice are all Pointers in nature, so nil can also be used to determine if these types are initialized and usable. Slice is special and has len and cap attributes, but does not affect nil:

Nil in interface, there’s a hidden trap.

The interface consists of two parts: the type of the interface and the value of the interface:

First look at the output of the following code:

var in io.Writer
fmt.Printf("%T\n", in)  // nil

var inP *io.Writer
fmt.Printf("%T\n", inP) // *io.Writer
Copy the code

%T indicates the type to output this value.

After declaring the above variables, the structure of in and inP looks like this:

If I write the following code again:

var in io.Writer
ifin ! =nil {
	  in.Write([]byte("logs"))}var inP *io.Writer
ifinP ! =nil {
		inP.Write([]byte("logs")) // There will be panic
}
Copy the code

Type of inP is not nil, then inP is not nil. When using interfaces, note that the interface is equal to nil only if both its type and value are nil, otherwise it is not equal.

Error handling is an important part of a Go program, but it’s also prone to pitfalls. Here’s the code:

func main(a) {
	err := Do()       // nil
	fmt.Printf("result: %+v\n", (err == nil)) // true
}

func Do(a) *DoError { // nil
	return nil
}

type DoError struct{}func (d *DoError) Error(a) string {
	return "doError"
}
Copy the code

The above code returns a pointer of type DoError instead of an interface, so it’s ok to determine if it’s nil.

In other words, if you look at the following code, it returns the error interface type, but the interface type is *DoError and the value is nil, so it doesn’t match the expected result.

func main(a) {
    err := Do()       // error(*DoError, nil)
    fmt.Printf("result: %+v\n", (err == nil)) // false
}

func Do(a) error {    // error(*DoError, nil)
   var err *DoError
   return err      // nil 
}
Copy the code

Determining whether err is nil is a very common usage scenario, and you need to be very careful when handling these errors.

3. Other functions of nil

Nil has other uses besides being a zero value.

Nil is perfectly legal as the recipient of a method, where p is nil of type *Person:

func main(a) {
	var p *Person // nil
	p.SayHi()
}

type Person struct{}func (p *Person) SayHi(a) {
	fmt.Println("hi")}Copy the code

Nil can also be used as a default, and I think we’ll see a little bit more in the following code, but normally, the second argument we’ll just pass nil, where nil means use the default configuration, and we can use that in our own code as well.

http.HandleFunc("localhost:8080".nil)
Copy the code

4. Summary

Nil is the zero value of pointer, channel, function, interface, slice, etc. The zero value of interface is a bit special. The interface is nil only if both type and value are nil.

Nil has many other uses besides being used as a zero value, such as being the recipient of a method and representing the default value.

The text/Rayjun

[1] www.youtube.com/watch?v=yno…