background
Hello, everyone. I'm asong. I saw a very interesting interview question in an exchange group a few days ago. Today I share it. Let's take a look at this question first:
fmt.Println(nil== nil)
What is the comparison result of two Nils?
true, false, or unable to compile? Let's think first and reveal the answer in the article.
Definition of nil in Go
In the official document of Go, nil is defined as follows:
// nil is a predeclared identifier representing the zero value for a // pointer, channel, func, interface, map, or slice type. var nil Type // Type must be a pointer, channel, func, interface, map, or slice type
Nil is a pre declared identifier representing pointer, channel, func, interface, map and slice. It can also be understood as follows: the zero value of pointer, channel, function, interface, map and slice is nil, just as the zero value of boolean type is false and the zero value of integer type is 0.
Deep understanding of nil
nil is not a keyword at all
Let's start with a piece of code:
func main() { nil := "this is nil" fmt.Println(nil) } // Operation results this is nil
How about this again?
func main() { nil := "this is nil" fmt.Println(nil) var slice []string = nil fmt.Println(slice) } // Operation results # command-line-arguments ./nil.go:10:6: cannot use nil (type string) as type []string in assignment
An error is reported directly when compiling. Because this nil is a string type, it is determined from here that nil is not a keyword in Go language. We can define the variable name as nil at will (but it is not recommended).
Default type of nil
Generally, pre declared identifiers have a default type. For example, the default type of itoa in Go language is int. what about the default type of nil? Let's write an example:
func main() { const val1 = iota fmt.Printf("%T\n",val1) var val2 = nil fmt.Printf("%T\n",val2) } // Operation results # command-line-arguments ./nil.go:10:6: use of untyped nil
An error has been reported at the time of compilation. The compiler tells us that typeless nil is used, so we can draw a conclusion:
Nil has no default type and its type is uncertain. When we use it, we must provide enough information to enable the compiler to infer the type expected by nil.
Comparison of nil
The comparison of nil can be divided into the following two cases:
- Comparison of nil identifiers
- Comparison of nil values
Let's first look at the comparison of nil identifiers, that is, the interview question at the beginning. Let's first look at the running results:
# command-line-arguments ./nil.go:8:18: invalid operation: nil == nil (operator == not defined on nil)
From the compilation results, we can see that the = = symbol is an undefined operation for nil, so it is impossible to compare two NILs.
Next, let's take a look at the value comparison of nil. Because nil has no type and is determined according to the context during compilation, to compare the value of nil is to compare different types of nil. This is divided into the comparison of nil values of the same type and the comparison of nil values of different types. Let's verify these two cases respectively.
- Comparison of nil values of the same type
func main() { // nil comparison of pointer types fmt.Println((*int64)(nil) == (*int64)(nil)) // nil comparison of channel types fmt.Println((chan int)(nil) == (chan int)(nil)) // nil comparison of func type fmt.Println((func())(nil) == (func())(nil)) // func() can only be compared with nil // nil comparison of interface type fmt.Println((interface{})(nil) == (interface{})(nil)) // nil comparison of map type fmt.Println((map[string]int)(nil) == (map[string]int)(nil)) // map can only be compared with nil // nil comparison of slice type fmt.Println(([]int)(nil) == ([]int)(nil)) // slice can only be compared with nil }
Operation results:
# command-line-arguments ./nil.go:13:28: invalid operation: (func())(nil) == (func())(nil) (func can only be compared to nil) ./nil.go:17:36: invalid operation: (map[string]int)(nil) == (map[string]int)(nil) (map can only be compared to nil) ./nil.go:19:27: invalid operation: ([]int)(nil) == ([]int)(nil) (slice can only be compared to nil)
From the running results, we can see that pointer type nil, channel type nil and interface type can be compared with each other, while func type, map type and slice type can only be compared with nil identifier. It is illegal to compare the two types with each other.
- Comparison of different types of nil values
func main() { var ptr *int64 = nil var cha chan int64 = nil var fun func() = nil var inter interface{} = nil var ma map[string]string = nil var slice []int64 = nil fmt.Println(ptr == cha) fmt.Println(ptr == fun) fmt.Println(ptr == inter) fmt.Println(ptr == ma) fmt.Println(ptr == slice) fmt.Println(cha == fun) fmt.Println(cha == inter) fmt.Println(cha == ma) fmt.Println(cha == slice) fmt.Println(fun == inter) fmt.Println(fun == ma) fmt.Println(fun == slice) fmt.Println(inter == ma) fmt.Println(inter == slice) fmt.Println(ma == slice) }
Operation results:
# command-line-arguments ./nil.go:14:18: invalid operation: ptr == cha (mismatched types *int64 and chan int64) ./nil.go:15:18: invalid operation: ptr == fun (mismatched types *int64 and func()) ./nil.go:17:18: invalid operation: ptr == ma (mismatched types *int64 and map[string]string) ./nil.go:18:18: invalid operation: ptr == slice (mismatched types *int64 and []int64) ./nil.go:20:18: invalid operation: cha == fun (mismatched types chan int64 and func()) ./nil.go:22:18: invalid operation: cha == ma (mismatched types chan int64 and map[string]string) ./nil.go:23:18: invalid operation: cha == slice (mismatched types chan int64 and []int64) ./nil.go:25:18: invalid operation: fun == inter (operator == not defined on func) ./nil.go:26:18: invalid operation: fun == ma (mismatched types func() and map[string]string) ./nil.go:27:18: invalid operation: fun == slice (mismatched types func() and []int64) ./nil.go:27:18: too many errors
From the running results, we can conclude that only pointer type and channel type can be compared with interface type, and other types cannot be compared with each other. Why can pointer type and channel type be compared with interface type?
This answer is left blank first, because I didn't figure it out. Doesn't it mean that / any type implements the interface {} type? I don't understand here. I look forward to your answer.
Problems needing attention in using nil in different types
One point to pay attention to when comparing interface with nil
Let's start with an example:
func main() { err := Todo() fmt.Println(err == nil) } type Err interface { } type err struct { Code int64 Msg string } func Todo() Err { var res *err return res } // Operation results false
The output result is false. In Todo method, we declare a variable res, which is a pointer type, the zero value is nil, and the return value is the interface type. Logically, the return value interface type should also be nil, but the result is not the same. This is because we ignore a concept of interface type. Interface is not a simple value, but is divided into type and value. Therefore, the nil judgment of the interface will be true only when the type and value are both nil.
This is an easy problem for novices. We must pay attention to this problem.
Will panic occur when reading and writing data from a nil map
For this problem, let's just write an example to test it:
func main() { var m map[string]string fmt.Println(m["asoong"]) m["asong"] = "Golang DreamWorks" } // Operation results panic: assignment to entry in nil map goroutine 1 [running]: main.main() go/src/asong.cloud/Golang_Dream/code_demo/slice_demo/nil.go:10 +0xed
According to the running results, we can see that a nil map can read data, but cannot write data, otherwise panic will occur. Therefore, if you want to use a map, you must use make for initialization.
Closing the channel of nil will cause panic
func main() { var cha chan int close(cha) }
Operation results:
panic: close of nil channel goroutine 1 [running]: main.main() /go/src/asong.cloud/Golang_Dream/code_demo/slice_demo/nil.go:5 +0x2a
According to the running results, we can conclude that closing a nil channel will lead to program panic. We should pay attention to this problem in use. There is another problem to pay attention to: reading and writing data in a nil channel will cause permanent blocking.
Precautions for using a slice for nil
func main() { var slice []int64 = nil fmt.Println(len(slice)) fmt.Println(cap(slice)) for range slice{ } fmt.Println(slice[0]) } // Operation results 0 0 panic: runtime error: index out of range [0] with length 0 goroutine 1 [running]: main.main() /go/src/asong.cloud/Golang_Dream/code_demo/slice_demo/nil.go:14 +0xf2
Based on this example, we can draw the following conclusions:
An index with nil cannot be indexed, otherwise panic will be triggered, and other operations are allowed.
Will panic be raised when the method receiver is nil
func main() { var m *man fmt.Println(m.GetName()) } type man struct { } func (m *man)GetName() string { return "asong" } // Operation results asong
According to the running results, we can see that when the method receiver is nil, we can still access the corresponding method, but we should pay attention to the writing in the method, otherwise panic will also be caused. The above code is changed as follows:
func main() { var m *man fmt.Println(m.GetName()) } type man struct { Name string } func (m *man)GetName() string { return m.Name } // Operation results panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a6ec3] goroutine 1 [running]: main.(*man).GetName(...) go/src/asong.cloud/Golang_Dream/code_demo/slice_demo/nil.go:18 main.main() go/src/asong.cloud/Golang_Dream/code_demo/slice_demo/nil.go:9 +0x23
This is to directly trigger panic, so we need to do a pointer null judgment for the sake of program robustness.
A null pointer is a pointer that has no value
func main() { var a = (*int64)(unsafe.Pointer(uintptr(0x0))) fmt.Println(a == nil) //true } // Operation results true
Here we do a small experiment with 0x0, which just proves that a null pointer is a pointer that does not point to any value.
summary
The article is drawing to a close. Let's reveal the answer to the beginning of the article. The knowledge points of nil comparison in the article can just answer this question. Nil identifier has no type, so = = is an undefined operation for nil and cannot be compared. This can be compared in python. In python, the two None values are always equal, Don't confuse your friends.
Finally, I suggest you take a look at this video: https://www.youtube.com/watch?v=ynoY2xz-F8s needs to climb over the wall. After reading this, you will have a new understanding of nil.