A few days ago a friend was learning go and asked me some questions about the interface mechanism. Try to explain to find yourself is not too clear, so this afternoon deliberately checked the information and read the source of Go (based on go1.4), sorted out this article. If there is a mistake, look at it.
The interface of Go language is one of my favorite features. Interface and structs can be converted to each other, the struct does not need to be like Java in the source code to display instructions to implement an interface, can be in the form of a contract, implicit conversion to interface, but also at runtime query interface type, so there is a dynamic language to write code feeling , but can be checked at compile time to catch some obvious type mismatch errors.
Type Stringer Interface { string () String}type S struct { i int}func (S *s) string () string { return FMT. Sprintf ("%d", s.i)}func Print (s Stringer) { println (s.string ())}func dynamicprint (Any interface{}) { if s, OK: = Any. (Stringer); OK { print (s) }}func main () { var s s s.i = 123456789 Print (&s) dynamicprint (&s)}
As shown in the code above, type S does not display the implementation of the Stringer interface, but its method list conforms to the Stringer interface, so it can be converted to Stringer interface for use.
So, how does the interface mechanism of the go language actually be implemented?
Interface value
The function print parameter in the code above is a stringer interface, which is an object instance of stringer. This object instance is called interface value. Its data structure is as follows:
Type iface struct { tab *itab data unsafe. Pointer}
Where the tab field is similar to C + +, Vptr,tab contains the corresponding method array, in addition to the type metadata that implements the interface. Data is the corresponding instance pointer to the type that implements the interface.
The ITAB data structure is as follows:
Type Itab struct { Inter *interfacetype _type *type link *itab bad int32 Unused int32 fun [0]unsafe. Pointer}
Where the Inter field represents the interface meta information that this interface value belongs to, the _type field represents the meta-information for the specific implementation type, and the Fun field represents the interface array of methods. link,bad,unused field temporarily does not care.
When we call a method of an interface in the go code, the operation is similar to the following: S.tab->fun[0] (s.data). The call cost is still very small.
How the Itab is generated
A custom struct can implement an interface, which can then be implicitly converted to the corresponding interface. This operation is somewhat like converting a derived class of C + + to a base class, which is a run-time binding process. The interface mechanism of the go language also has some other features: for example, a specific type can implement N-multiple methods, but only some or all of them satisfy an interface, and at this time, it is not possible to put all the methods in the Itab, which means that the binding process to eliminate some unwanted methods.
The go compiler generates a type metadata for each custom struct and interface type at compile time to describe the name of the type, the hash value of the type, the method list of the type, and the name of the method in the method list. When a custom struct is converted to a interface type, the go compiler generates code that calculates itab at run time, completing the need for a dynamic binding method. The process of calculating itab is relatively straightforward because the go compiler generates the type metadata that contains all the method names and addresses, so when a struct instance is converted to interface value, only the interface method list is used as the base. Method name and method type as key, go to the structure metadata to find the corresponding method.
The Go Runtime Library has been optimized for Itab's discovery process, with O (NI * nt) complexity changed to O (Ni + nt). The method based on which a custom struct is implemented must be a method set that is greater than or equal to a specific interface. So you can sort all the methods in advance by name from small to large, and then after matching to a method, you can use the last index value on the next lookup.
In addition, the go compiler adds a corresponding Itab cache in order to reduce the need for each itab. You can compile a go program and then decompile to see a variable like the Go_itab__main_s_main_stringer name. Check that the cache is valid for use before each struct is converted to a interface. This check is just a CMP directive.
Also in the Go Runtime library, in order to reduce each ITAB implementation, but also to do the corresponding optimization. The internal implementation of a hash table, saving each concrete structure to interface conversion generated ITAB instances. The code can be seen in the Go\src\runtime\iface.go getitab function.
Special handling of interface{}
interface{} is a special built-in type in go, similar to the void* in C/s + +, but contains type information. So you can convert any data to interface{} and get the original data from interface{} with type Assert. But as you can see, interface{} has no method, that is to say, it does not need itab in Iface, because method bindings are not required. For this, a special modification is made, and the tab field type in Iface is changed from the itab pointer to the corresponding type metadata pointer of the specific implementation type. In Go source, the type prototype of the interface{} object is as follows:
Type eface struct { _type *_type data unsafe. Pointer}
Eface is the abbreviation of empty interface.
Other
In the Go source code iface.go, you can also see a lot of functions such as called ASSERTE2E,ASSERTE2I,ASSERTE2T, these functions are the corresponding type assert the specific implementation function. E means that eface,i indicates that iface,t represents a custom struct or a type created from a built-in type. The code is relatively simple, not in the narrative.
Summarize
To understand the implementation of the interface mechanism, you only need to understand the type metadata and the dynamic binding process. It also distinguishes interface value, which is the internal iface structure. Therefore, the concept of itable is introduced. Overall is not too complex, data structure is relatively simple, if you have time, you can also see the source of go.
Reference
Go Source (go\src\runtime\iface.go)
"Go Data structures:interfaces"
"Go Interfaces"
Analysis on the interface mechanism of Go language