Interfaces in Golang are divided into methods and empty interfaces. Interfaces with methods are represented by iFace at the bottom level, and empty interfaces are represented by eface at the bottom level. Let’s take a look at the underlying principles of these two types of interfaces.

Here is a prototype interface:

// Runtime /runtime2.go // Non-null interfacetype iface struct {
	tab  *itab
	data unsafe.Pointer
}
type itab struct {
	inter  *interfacetype
	_type  *_type
	link   *itab
	hash   uint32 // copy of _type.hash. Used for type switches.
	bad    bool   // type does not implement interface
	inhash bool   // has this itab been added to hash? Unused [2] byte fun [1] uintptr / / variable sized} / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / air interfacetypeEface struct {_type * _type data unsafe. Pointer} / / = = = = = = = = = = = = = = = = = = = = = = = = / / the two interface _type common field //======================== //runtime/type.gotype _type struct {
	size       uintptr
	ptrdata    uintptr // size of memory prefix holding all pointers
	hash       uint32
	tflag      tflag
	align      uint8
	fieldalign uint8
	kind       uint8
	alg        *typeAlg
	// gcdata stores the GC type data for the garbage collector.
	// If the KindGCProg bit is set in kind, gcdata is a GC program.
	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
	gcdata    *byte
	str       nameOff
	ptrToThis typeOff} // The _type structure is used by Golang to define data types, which will be explained in more detail in the reflection article.Copy the code

1.iface

1.1 How are variable types converted to interface types?

Look at the code below:

package main
type Person interface {
   run()
}

type xitehip struct {
   age uint8
}
func (o xitehip)run() {
}

func main()  {
   var xh Person = xitehip{age:18}
   xh.run()
}

Copy the code

The xH variable is the Person interface type. How does xitehip’s struct type get converted to the interface type? Take a look at the generated assembly code:

0x001d 00029 (main.go:13)	PCDATA	$2.$0
0x001d 00029 (main.go:13)	PCDATA	$0.$0
0x001d 00029 (main.go:13)	MOVB	$0.""..autotmp_1+39(SP)
0x0022 00034 (main.go:13)	MOVB	$18.""..autotmp_1+39(SP)
0x0027 00039 (main.go:13)	PCDATA	$2.The $1
0x0027 00039 (main.go:13)	LEAQ	go.itab."".xitehip,"".Person(SB), AX
0x002e 00046 (main.go:13)	PCDATA	$2.$0
0x002e 00046 (main.go:13)	MOVQ	AX, (SP)
0x0032 00050 (main.go:13)	PCDATA	$2.The $1
0x0032 00050 (main.go:13)	LEAQ	""..autotmp_1+39(SP), AX
0x0037 00055 (main.go:13)	PCDATA	$2.$0
0x0037 00055 (main.go:13)	MOVQ	AX, 8(SP)
0x003c 00060 (main.go:13)	CALL	runtime.convT2Inoptr(SB)
0x0041 00065 (main.go:13)	MOVQ	16(SP), AX
0x0046 00070 (main.go:13)	PCDATA	$2.$2
0x0046 00070 (main.go:13)	MOVQ	24(SP), CX
Copy the code

ConvT2Inoptr (SB) convert runtime.convt2inoptr (SB) to runtime.convt2inoptr (SB)

func convT2Inoptr(tab *itab, elem unsafe.Pointer) (i iface) {
        t := tab._type
        if raceenabled {
                raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Inoptr))
        }
        if msanenabled {
                msanread(elem, t.size)
        }
        x := mallocgc(t.size, t, false// Memmove (x, elem, t.size)// Assign the data pointed by elem to the new memory. // set iface's TABreturn
}
Copy the code

From the above implementation we can see that the original struct data generated by the compiler is copied, and the new data address is assigned to iface. Data to generate the full iFace, so that the xH in the following original code is converted to the Person interface type.

var xh Person = xitehip{age:18}
Copy the code

See this in action with GDB (see Figure 1) :

The arguments to convT2Inoptr are *itab and *xitehip in the source code. Figure 2 shows the itAB type prototype and in-memory data discovery that itAB is indeed a field in the runtime source code. That’s 32 bytes. ([4] Uint8 不 用 bytes)

1.2 How are pointer variable types converted to interface types?

Or is the above example just going to

var xh Person = xitehip{age:18}
Copy the code

Replaced with a

var xh Person = &xitehip{age:18}
Copy the code

How can a pointer type variable be converted to an interface type? See the assembly code below:

0x001d 00029 (main.go:13)	PCDATA	$2.The $1
0x001d 00029 (main.go:13)	PCDATA	$0.$0
0x001d 00029 (main.go:13)	LEAQ	type."".xitehip(SB), AX
0x0024 00036 (main.go:13)	PCDATA	$2.$0
0x0024 00036 (main.go:13)	MOVQ	AX, (SP)
0x0028 00040 (main.go:13)	CALL	runtime.newobject(SB)
0x002d 00045 (main.go:13)	PCDATA	$2.The $1
0x002d 00045 (main.go:13)	MOVQ	8(SP), AX
0x0032 00050 (main.go:13)	MOVB	$18, (AX)
Copy the code

We found this function:

runtime.newobject(SB)
Copy the code

Let’s look at the implementation:

// implementation of new builtin
// compiler (both frontend and SSA backend) knows the signature
// of this function
func newobject(typ *_type) unsafe.Pointer {
        return mallocgc(typ.size, typ, true)}Copy the code

The compiler automatically generates iFace and assigns the address (via newObject) of the object created by &xitehip{age:18} to iFace.data. The structure xitehip was not copied. See figure 5 using GDB:

1.3 How does XH find the run method? We proceed to Figure 6, where relevant explanations have been noted:

1.4 Interface Invocation Rules

Add an eat() interface method to the above example and implement it (note that the recipient of this interface method’s implementation is a pointer).

package main
type Person interface {
	run()
	eat(string)
}
type xitehip struct {
	age uint8
}
func (o xitehip)run() {// // receiver o is the value} func (o *xitehip)eat(food string) {// receiver O is the pointer} funcmain() {var Person = &xitehip{age:18}"ma la xiao long xia!")
	xh.run()
}
Copy the code

The actual type of the xh variable in this example is a pointer, so how does it call the non-pointer method run? Continue the GDB trace, as shown in Figure 7:

In summary, an object of pointer type calls a recipient’s method of non-pointer type, and the compiler automatically converts the recipient to pointer type. The caller finds the corresponding list of method instructions through the xh.tab.fun array.

Xh is an interface of value type, and the receiver of the method implemented by the interface is a pointer type. Can the caller call the pointer method?

The caller The receiving party Can you compile
value value true
value Pointer to the false
Pointer to the value true
Pointer to the Pointer to the true
Pointer to the Pointers and value true
value Pointers and value false

The following conclusions can be drawn from the above table:

When the caller is a value, as long as the receiver has a pointer method, the compiler will not allow it to pass compilation.

2 eface

An empty interface has no method list relative to a non-empty interface.

type eface struct {
	_type *_type
	data  unsafe.Pointer
}
Copy the code

The first property is changed from itab to _type. This structure is the basis of the variable types in Golang, so the empty interface can specify any variable type.

2.1 the sample:

cpackage main

import "fmt"

type xitehip struct {
}
func main()  {
	var a interface{} = xitehip{}
	var b interface{} = &xitehip{}
	fmt.Println(a)
	fmt.Println(b)
}
Copy the code

See Figure 12 for GDB:

2.2 assertions

Determine the variable data type

   s, ok := i.(TypeName)
    if ok {
        fmt.Println(s)
    }
Copy the code

If the type is incorrect without OK, it will cause panic.

You can also use the switch form:

    switch v := v.(type) {
      case TypeName:
    ...
    }
Copy the code

3 Checking Ports

3.1 Use the compiler to check the interface implementation

var _ InterfaceName = (*TypeName)(nil)

3.2 Nil and nil Interface

3.2.1 nil
func main() {
    var i interface{}
    ifI == nil {println(" The interface is nil. ")}} (GDB) info locals; i = {_type = 0x0, data = 0x0}Copy the code
3.2.2 If the internal data value of the interface is nil but TAB is not null, then the interface is nil interface.
// go:noinline
func main() {
    var o *int = nil
    var i interface{} = o

    if i == nil {
        println("Nil")
    }
    println(i)
}

(gdb) info locals;
i = {_type = 0x21432f8 <type.*+36723>, data = 0x0}
o = 0x0
Copy the code
3.2.3 Use reflection check
  v := reflect.ValueOf(a)
    if v.Isvalid() {
        println(v.IsNil()) // true, This is nil interface
}
Copy the code

reference

Go Interface implementation analysis — Xiaomi Cloud technology

Deep decryption of interface problems in Go

Go Interface source code analysis