
The basic concept of interface is not repeated here , Details please see Chapter 16 : Interface
nil Non empty ?
package main
func main() {
var obj interface{}
obj = 1
println(obj == 1) // true
obj = "hello"
println(obj == "hello") // true
type User struct {
}
var u *User
obj = u
println(u == nil) // true
println(obj == nil) // true
}
The previous one is just a comparison , explain interface can hold everything. The last two judgments we need to pay attention to :
uIt's aUserNull pointer of type ,println(u == nil)OutputtrueIs expected ;- take
uAssign a value toobjafter ,println(obj == nil)The output isfalse, unexpected
Why put the null pointer u Assign a value to interface after ,obj It's not nil Did you? ? What could it be ?
adopt gdb Tool debugging , We see interface It turned out to be like this :
(gdb) ptype obj
type = struct runtime.eface {
runtime._type *_type;
void *data;
}
adopt goland Take a look at obj What's in it

You can see it data It's used to store data ,_type For storage type :
- When
obj = 1when , At the bottomefaceBoth properties of have values ; - When
obj = uwhen , At the bottomefaceOfdataAttribute is empty ,_typeProperty is not empty - When
obj = nilwhen , At the bottomefaceOfdataand_typeAll of them are empty
Comparison of corresponding structure types , Two variables are equal only when all fields in the structure are equal , because eface Of _type Belongs to non empty , So when will u Assign a value to obj after ,println(obj == nil The output is false.
This raises another question , When executed
obj = uThis line of code is ,golang runtime How to put the value of static typeuconvert toefaceStructural ?
When assigning a value to an interface
Then the question above , We use the following simple code , See how to convert a static type value to eface Of
package main
import "fmt"
func main() {
var a int64 = 123
var i interface{} = a // This line is converted
fmt.Println(i)
}
Through the command go tool compile -N -l -S main.go Turn it into assembly code 
In the red box is the number 7 The assembly corresponding to line refers to CALL runtime.convT64(SB)( Assembly code can directly call Go func), We can do it in runtime Find the corresponding function in the package
// runtime/iface.go
func convT64(val uint64) (x unsafe.Pointer) {
if val < uint64(len(staticuint64s)) {
x = unsafe.Pointer(&staticuint64s[val])
} else {
x = mallocgc(8, uint64Type, false) // Allocate memory ,(size, _type, needzero)
*(*uint64)(x) = val // Copy
}
return
}
eface, iface
Through the above experiment , We have learned that the underlying structure of the interface is eface. actually ,Golang Depending on whether the interface contains methods , Divide the interfaces into two categories :
eface: Interfaces that do not contain any binding methods- such as : Empty interface
interface{}
- such as : Empty interface
iface: Interface containing binding methods- such as :os.Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
eface
eface Data structure of :
type eface struct {
_type *_type
data unsafe.Pointer
}
We should be familiar with this , In the above experiment, we have seen :_type and data attribute , Represents the underlying type information and the value information pointer respectively .
Let's take a look _type attribute , Its type is another structure :
type _type struct {
size uintptr // The size of the type
ptrdata uintptr // The size of the memory prefix that contains all the pointers
hash uint32 // Type of hash value , Here, calculate ahead of time , You can avoid calculating in the hash table
tflag tflag // Additional type information flags , Here is the type of flag sign , Mainly for reflection
align uint8 // The corresponding variable is aligned with the memory size of the type
fieldAlign uint8 // The memory alignment size of the structure of the corresponding type
kind uint8 // Enumeration value of type , contain Go All types in language , for example :`kindBool`、`kindInt`、`kindInt8`、`kindInt16` etc.
equal func(unsafe.Pointer, unsafe.Pointer) bool // Callback function to compare this object
gcdata *byte // To store the garbage collector GC Type data
str nameOff
ptrToThis typeOff
}
In conclusion :runtime Just check here , You can get all the information related to the type ( Byte size 、 Type mark 、 Memory alignment, etc ).
iface
iface Data structure of :
type iface struct {
tab *itab
data unsafe.Pointer
}
And iface comparison , Their data The attributes are the same , For storing data ; The difference is , because iface Not only store type information , Also store the method of interface binding , All required itab Structure to store both information . Let's see itab:
type itab struct {
inter *interfacetype // Interface type information
_type *_type // Specific type information
hash uint32 // _type.hash Copy of , It is used to compare and judge the type of target type and interface variable
_ [4]byte
fun [1]uintptr // The address of the specific implementation of the method set of the storage interface , It contains a set of function pointers , The dynamic dispatch of interface method is realized , And every time when the interface changes
}
Summing up , The data structure of the interface is simple , Type and value descriptions . And then according to the specific differences , For example, whether to include a method set , Specific interface types, etc .
iface, Interface bound method Where did you save it ?
Through the last section , We know iface Methods that can store interface bindings . It can also be seen from its structure iface.tab.fun Fields are used to do this . however , I have a question :fun The type is of length 1 Pointer array of , Can it only save one method?
type Animal interface {
Speak () string
Move()
Attack()
}
type Lion struct {
}
func (l Lion) Speak() string {
return "Uh....."
}
func (l Lion) Move() {
}
func (l Lion) Attack() {
}
func main() {
lion := Lion{}
var obj interface{} = lion
cc, _ := obj.(Animal)
fmt.Println(cc.Speak()) // Un....
}
Lion Is an implementation of the interface Animal The structure of all methods , So an interface obj Try to convert to by type assertion Animal Interface is , It can be successful . adopt Debug debugging , When I perform cc, _ := obj.(Animal) This line of code is , Internal adjustment assertE2I2 Method and then returns
func assertE2I2(inter *interfacetype, e eface) (r iface, b bool) {
t := e._type
if t == nil {
return
}
tab := getitab(inter, t, true)
if tab == nil {
return
}
r.tab = tab
r.data = e.data
b = true
return
}
So back cc The variable is actually a iface Structure , because iface Unable to export. We can't see the internal data , But we can go through the main In the program iface Structure defines a , Convert through pointer operation :
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
...
func main() {
lion := Lion{}
var obj interface{} = lion
cc, _ := obj.(Animal)
fmt.Println(cc.Speak()) // Uh.....
dd := *(*iface)(unsafe.Pointer(&cc)) // When cc Turn into iface Interface body
fmt.Printf("%v\n", dd)
fmt.Printf("%+V", cc)
}
adopt debug You can see , Interface Animal Corresponding eface A complete data 
tab It stores the data of types and binding methods :inter.mhdr The length of is 3, It seems to be stored 3 Names and types of methods ,fun A pointer is stored in , It should be the address of the first method . The following code can confirm :
// itab The initialization
func (m *itab) init() string {
inter := m.inter
typ := m._type
x := typ.uncommon()
// ni The value of is the number of methods bound by the interface
ni := len(inter.mhdr)
nt := int(x.mcount)
// I guess xmhdr Is where the methods of the real storage interface
xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]
j := 0
methods := (*[1 << 16]unsafe.Pointer)(unsafe.Pointer(&m.fun[0]))[:ni:ni]
var fun0 unsafe.Pointer
imethods:
// Traverse 3 A plan
for k := 0; k < ni; k++ {
i := &inter.mhdr[k]
itype := inter.typ.typeOff(i.ityp)
name := inter.typ.nameOff(i.name)
iname := name.name()
ipkg := name.pkgPath()
if ipkg == "" {
ipkg = inter.pkgpath.name()
}
for ; j < nt; j++ {
t := &xmhdr[j]
tname := typ.nameOff(t.name)
// By traversing xmhdr, If and mhrd[k] Name 、 Type and pkgpath All equal , I found it
if typ.typeOff(t.mtyp) == itype && tname.name() == iname {
pkgPath := tname.pkgPath()
if pkgPath == "" {
pkgPath = typ.nameOff(x.pkgpath).name()
}
if tname.isExported() || pkgPath == ipkg {
if m != nil {
// Gets the address of the method
ifn := typ.textOff(t.ifn)
if k == 0 {
// Record the address of the first method
fun0 = ifn // we'll set m.fun[0] at the end
} else {
methods[k] = ifn
}
}
continue imethods
}
}
}
// didn't find method
m.fun[0] = 0
return iname
}
// func[0] = Address of the first method
m.fun[0] = uintptr(fun0)
return ""
}
To sum up , In putting an uncertain interface{} When a type is asserted as a specific interface ,runtime The original data will be 、 Methods to iface Data structure .iface In fact, only the address of the first method is saved , Other methods can be found by offset , The offset information is saved in mhdr in ( To be verified )
How does type assertion work
Go It's a strong type of language , Variable type 、 The type of function parameter must be defined, so it cannot be transformed . This provides a safe and stable guarantee for the type of program , But it also brings more workload to the coding of the program . For example, we are going to implement an addition function , You need to write about different types , And it is inconvenient to use :
func addInt(a, b int) int { return a + b }
func addInt32(a, b int32) int32 { return a + b }
func addInt64(a, b int64) int64 { return a + b }
func addFloat32(a, b float32) float32 { return a + b }
func addFloat64(a, b float64) float64 { return a + b }
be based on interface can hold everything, We use interface{} When the input parameter type , With a function :
func add(a, b interface{}) interface{} {
switch av := a.(type) {
case int:
if bv, ok := b.(int); ok {
return av + bv
}
panic("bv is not int")
case int32:
if bv, ok := b.(int32); ok {
return av + bv
}
panic("bv is not int32")
...
case float64:
if bv, ok := b.(float64); ok {
return av + bv
}
panic("bv is not float64")
}
panic("illegal a and b")
}
func main() {
var a int64 = 1
var b int64 = 4
c := add(a, b)
fmt.Println(c) // 5
}
Someone may ask :add The parameter variable type of the function is interface{} 了 , It is in the function how to get from interface{} With variables in ?( The answer is eface)
First step
int64->efaceNotice this line of code
c := add(a, b), Translated into compilation :0x002f 00047 (main.go:132) FUNCDATA $2, "".main.stkobj(SB)
0x002f 00047 (main.go:142) MOVQ $1, "".a+56(SP)
0x0038 00056 (main.go:143) MOVQ $4, "".b+48(SP)
0x0041 00065 (main.go:144) MOVQ "".a+56(SP), AX
0x0046 00070 (main.go:144) MOVQ AX, (SP)
0x004a 00074 (main.go:144) PCDATA $1, $0
0x004a 00074 (main.go:144) CALL runtime.convT64(SB)
Pay attention to the last line
runtime.convT64, As mentioned above , The operation here copies a value to the functionaddfunc convT64(val uint64) (x unsafe.Pointer) {
if val < uint64(len(staticuint64s)) {
x = unsafe.Pointer(&staticuint64s[val])
} else {
x = mallocgc(8, uint64Type, false)
*(*uint64)(x) = val
}
return
}
The second step is from
efaceGet type informationTo test our conjecture , We are
addThe function entry convertsinterface{} aTurn intoeface ddLet's see what its specific data looks likefunc add(a, b interface{}) interface{} {
dd := *(*eface)(unsafe.Pointer(&a))
fmt.Println(dd)
switch av := a.(type) {
case int:
if bv, ok := b.(int); ok {
return av + bv
}
panic("bv is not int")
}
...
adopt debug What you see dd The data are as follows :

Be carefuldd._type.kindThe field is only 6, staysrc/runtime/typekind.goIn file , Maintain a constant for each typeconst (
kindBool = 1 + iota
kindInt
kindInt8
kindInt16
kindInt32
kindInt64 // 6
kindUint
kindUint8
kindUint16
kindUint32
kindUint64
kindUintptr
kindFloat32
...
)
You can see ,
int64The corresponding constant value is exactly 6. This explains that getting through type assertions willinterface{}The principle of turning into a specific type .
summary
Function of interface
- stay Go Runtime , For the convenience of internal data transmission 、 Operational data , Use
interface{}As a medium for storing data , Greatly reduced development costs . This medium storesThe location of the data、Type of data, There are two messages , Can represent all variables , namelyinterface can hold everything. - Interface is also an abstract capability , By defining the method to be implemented by an interface , It's equivalent to
How to judge this struct Is it this kind of interfaceComplete a clear definition , That is, it must be all methods of interface binding . Through this ability , It can be decoupled to a great extent in coding , Interfaces are like agreements between upstream and downstream developers .
There are two types of internal storage of interfaces
Golang Depending on whether the interface contains methods , Divide the interfaces into two categories :
eface: Interfaces that do not contain any binding methods- such as : Empty interface
interface{}
- such as : Empty interface
iface: Interface containing binding methods- such as :os.Writer
The difference between the two lies in eface Save the method information of interface binding .
watch out , After becoming an interface , It is not allowed to judge the space
The condition for judging null is that all fields of the structure are nil Talent , When nil After the fixed type value of is converted to the interface , The data value of the interface is nil, however type Values are not for nil It will lead to the failure of air judgment .

The solution is : Do not write out the interface type for the return parameter of the function , Judge the air first on the outside , Turning into an interface .
Go Interface : More related articles that go deep into the internal principles
- Android Thread management ( 3、 ... and )——Thread The inner principle of a class 、 Sleep and wake up
Thread communication .ActivityThread And Thread Class is understanding Android The key to thread management . Threads , As CPU The basic unit of scheduling resources , stay Android And other operating systems for embedded devices , It plays a very important and fundamental role . ...
- 【 turn 】SQLServer internals
Original address :http://twb.iteye.com/blog/182083 Talking about SQLSERVER Before the internal principle , I think it is very necessary to introduce to you SQLSERVER The history of . Let's stand here 1999 year , Look at the plan ...
- asp.net internals 3
asp.net internals ( 3、 ... and ) Third edition ( The most detailed version ) Preface : Today, let's continue this series , The company's projects are busy these days , I'm very tired when I get home , So I didn't bother to come here every day to share some things and discuss with you , But tonight ...
- JVM internals
This article describes in detail Java The internal structure of virtual machine . Here's a picture from <The Java Virtual Machine Specification Java SE 7 Edition>, It shows a ...
- JVM internals ( 6、 ... and )— Java One of the bases of bytecode
JVM internals ( 6、 ... and )- Java One of the bases of bytecode Introduce edition :Java SE 7 Why need to know Java Bytecode ? Whether you are a Java developer . Architects .CxO Or ordinary users of smart phones ,Java ...
- JVM internals ( Four )— Basic concepts JVM structure
JVM internals ( Four )- Basic concepts JVM structure Introduce edition :Java SE 7 Use per person Java All of the programmers know Java The bytecode is Java Runtime (JRE - Java Runtime En ...
- JVM internals ( 3、 ... and )— File formats such as basic concepts
JVM internals ( 3、 ... and )- File formats such as basic concepts Introduce edition :Java SE 7 Use per person Java All of the programmers know Java The bytecode is Java Runtime (JRE - Java Runtime Envi ...
- JVM internals ( Two )— The basic concept of bytecode
JVM internals ( Two )- The basic concept of bytecode Introduce edition :Java SE 7 Use per person Java All of the programmers know Java The bytecode is Java Runtime (JRE - Java Runtime Enviro ...
- JVM internals ( One )— summary
JVM internals ( One )- summary Introduce edition :Java SE 7 The components shown in the figure will be explained in two ways . The first part covers components unique to threads , The second part covers thread independent components ( Thread shared components ). Catalog Thread exclusive (Thr ...
- Apache Storm Internal principle analysis
from :http://shiyanjun.cn/archives/1472.html This article is personal to Storm A summary of application and learning , Because I don't understand Clojure Language , So we can't analyze more from the source code , But refer to the official website ...
Random recommendation
- utilize BitLocker and vhdx Create an encrypted Win10 System
If the computer does not support TPM encryption BitLocker, The system disk cannot be fully encrypted . You can take a flexible approach : Create a vhdx, Run this virtual disk BitLocker encryption , Then install the operating system on this disk , Finally, put vhdx ...
- SVM A little bit of
SVM 1. Ordinary SVM The classification function of can be expressed as : among ai Is the parameter to be optimized , The physical meaning is the weight of support vector samples ,yi Used to represent the attributes of training samples , Positive sample or negative sample , To calculate the kernel function of the inner product ,b Is the parameter to be optimized . The optimization objective function is : ...
- iOS Various value transfer methods
The attribute values take A The information owned by the page is passed to B Page using B The page defines a naviTitle attribute , stay A Page directly through the property assignment will A The values in the page are passed to B page . A page DetailViewController.h ...
- tomcat Administrators manager app Can't get into the solution
Browser input http://localhost:8080/ Get into tomcat After the page , Click on manager app enter one user name (admin) password (admin) After the page Jump, the following error appears : remarks :tomcat7.0.39 ...
- Java Study ( One )
All sorts of reasons start java It's almost a month since the development . There has been no official record of ... now ..O(∩_∩)O~.... Let's first summarize some problems we have encountered since we studied : 1.myeclipse Common shortcut keys for : F2 When the mouse is placed on a marker ...
- lua Batch rename files
local s = io.popen("dir F:\\headicon /b/s") local filelist = s:read("*all") loca ...
- Single source of graph Dijkstra Algorithm 、 Shortest path algorithm with negative weight
1. Basic composition of graph class The base item stored in the adjacency table /** * Represents an edge in the graph * */ class Edge implements Comparable< ...
- Simple php Code trace debug implementation
Simple php Code trace debug implementation debug_backtrace: Generate backtracking debug_print_backtrace: Print backtracking 1. debug_backtrace ($options = DEBUG ...
- machine learning - Stochastic gradient descent (Stochastic gradient descent)
sklearn actual combat - Breast cancer data mining ( The blogger recorded the video himself ) https://study.163.com/course/introduction.htm?courseId=1005269003& ...
- ES6 Take a walk Generator Asynchronous applications
Generator Asynchronous application of function JS Asynchronous programming callback Promise( Solve callback hell ) event Publish subscribe generator Thunk function Butt function Two higher-order functions The input parameter of the first call ...



![[in simple terms] from self information to entropy, from relative entropy to cross entropy, nn Crossentropyloss, cross entropy loss function and softmax, multi label classification](/img/42/5419f0b3240142de87895439bd4297.png)



10": potential combination of no code / low code and RPA Technology"/>

