I recently stepped into a hole in my project – “Golang interface that contains nil pointer is not nil interface”. The problem is that the function returns nil to an object, and the value returned by interface is never nil. If you don’t quite understand this sentence, it is recommended that you take a look at the sample code below to avoid future code stumbles.

The sample a

Let’s take a look at this code. Do you have any questions?

type IPeople interface {
	hello()
}
type People struct{}func (p *People) hello(a) {
	fmt.Println("github.com/meetbetter")}func errFunc1(in int) *People {
	if in == 0 {
		fmt.Println("ImportantFunc returns a nil")
		return nil
	} else {
		fmt.Println("ImportantFunc returns a non-nil value")
		return &People{}
	}

}

func main(a) {
	var i IPeople

	in := 0

	i = errFunc1(in)

	if i == nil {

		fmt.Println("Ha, external receive is nil.")}else {

		fmt.Println("Gee, external reception is not nil.")
		fmt.Printf("%v, %T\n", i, i)
	}

}
Copy the code

The result of this code is:

ImportantFunc returns nil. <nil>, * main.peopleCopy the code

You can see that in main you don’t get nil, but in errFunc1() you get nil, so why isn’t in main you get nil? This is because: Assigning nil to *People and then assigning *People to interface, *People itself is a pointer to nil, but assigning it to the interface is just that the value in the interface is nil, but the type information in the interface is *main.People instead of nil, So this interface is not nil. Golang’s interface type contains two parts of information — value information and type information. An interface is nil only if its value merge type is nil. The underlying implementation of interface can be seen in the source code analysis later.

The correct way to handle an interface return value is to assign nil directly to interface:


func rightFunc(in int) IPeople {
	if in == 0 {
		fmt.Println("ImportantFunc returns a nil")
		return nil
	} else {
		fmt.Println("ImportantFunc returns a non-nil value")
		return &People{}
	}

}
Copy the code

Example 2

An interface that contains nil Pointers is not a nil interface:

type IPeople interface {
	hello()
}
type People struct{}func (p *People) hello(a) {
	fmt.Println("github.com/meetbetter")}// error: The interface is not nil if you give people nil to the empty interface, because the value in interface is nil but the type is not nil

func errFunc(a) *People {

	return nil
}

// Handle properly the method that returns nil to the interface, and when it returns, go determines whether the interface is nil
func rightFunc(a) IPeople {

	return nil
}
func main(a) {

	var i IPeople
	i = errFunc()
	if i == nil { // If the interface is nil, the interface is not null

		fmt.Println(ErrFunc by the way, external receive is nil)
		fmt.Println(reflect.TypeOf(i))
	} else {

		fmt.Println("ErrFunc is wrong, external receiver is not nil.")
		fmt.Println(reflect.TypeOf(i))
	}

	i = rightFunc()
	if i == nil {

		fmt.Println("RightFunc by the way, external receiver is nil.")
		fmt.Println(reflect.TypeOf(i))
	} else {

		fmt.Println("RightFunc is wrong, external receiver is not nil.")
		fmt.Println(reflect.TypeOf(i))

	}

}
Copy the code

Output result:

*main.People rightFunc is also nil <nil>Copy the code

Interface low-level implementation

Itab stores _type information and the set of []fun methods. So even if data points to nil, it doesn’t mean interface is nil. Also consider the _type information.

type eface struct {      / / air interface
    _type *_type         // Type information
    data  unsafe.Pointer // Pointers to data (the special Pointer type in go, unsafe.Pointer, is similar to void* in C)
}
type iface struct {      // Interface with methods
    tab  *itab           // Store type information and a collection of structure implementation methods
    data unsafe.Pointer  // Pointers to data (the special Pointer type in go, unsafe.Pointer, is similar to void* in C)
}
type _type struct {
    size       uintptr  // Type size
    ptrdata    uintptr  // The prefix holds the memory size of all Pointers
    hash       uint32   // Data hash value
    tflag      tflag
    align      uint8    / / alignment
    fieldalign uint8    // The alignment of the embedded structure
    kind       uint8    //kind Some enumerations of kind equal to 0 are invalid
    alg        *typeAlg // Array of function Pointers, all methods of type implementation
    gcdata    *byte
    str       nameOff
    ptrToThis typeOff
}
type itab struct {
    inter  *interfacetype  // Interface type
    _type  *_type          // Structure type
    link   *itab
    bad    int32
    inhash int32
    fun    [1]uintptr      // Set of variable size methods
}
Copy the code

The complete code above is organized in Github- learn Golang project with sample code.

Reference article:

Golang first pit

“An interface that contains nil Pointers is not a nil interface” discussion