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