This article is still very long, basically covers all aspects of interface, there are examples, source code analysis, assembly analysis, write more than 20 days. There are still some things that are not covered, for example, reflection is not covered in the article, of course, I will write a separate article on reflection later, that is the next part.

I still hope you can gain something after reading this article. If you have any questions or suggestions, please leave a message at the back of this article.

The structure of this article is relatively simple. It directly raises 10 questions and answers one by one.


1. The relationship between Go language and duck type

Let’s get straight to the definition from Wikipedia:

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

Translation: If something looks like a duck, swims like a duck, quacks like a duck, it can be considered a duck.

Duck Typing, Duck Typing, is an object inference strategy for dynamic programming languages that focuses more on how objects can be used than on the types of objects themselves. Go is a static language that perfectly supports duck types through interfaces.

For example, in the dynamic language Python, define a function like this:

def hello_world(coder):    coder.say_hello()Copy the code

When you call this function, you can pass in any type, as long as it implements the say_hello() function. If it is not implemented, an error occurs during the run.

In static languages such as Java and C++, an interface must be explicitly declared to be implemented before it can be used wherever it is needed. If you call hello_world in a program and pass in a type that does not implement say_hello() at all, it will not pass at compile time. This is why static languages are safer than dynamic ones.

This is where the difference between dynamic and static languages comes in. Static languages can detect type mismatches at compile time, unlike dynamic languages, which have to run to that line of code to report an error. By the way, this is one of the reasons I don’t like python. Of course, static languages require programmers to write programs according to rules during the coding phase, specifying data types for each variable, which, to some extent, increases the amount of work and the amount of code. Dynamic languages don’t have these requirements, allowing you to focus on the business, and the code is shorter and faster to write, as those of you who write Python know.

As a modern static language, Go has the advantage of being a late mover. It introduces the convenience of dynamic languages, while doing type checking for static languages, and is very Happy to write. Go takes a middle ground: it doesn’t require a type to explicitly declare that an interface is implemented, as long as the associated methods are implemented, and the compiler can detect them.

Here’s an example:

Define an interface and a function that takes this interface as an argument:

type IGreeting interface {    sayHello()}func sayHello(i IGreeting) {    i.sayHello()}Copy the code

Let’s define two more structures:

type Go struct {}func (g Go) sayHello() {fmt.Println("Hi, I am GO!" )}type PHP struct {}func (p PHP) sayHello() {fmt.Println("Hi, I am PHP!" )}Copy the code

Finally, call sayHello() inside main:

func main() {    golang := Go{}    php := PHP{}    sayHello(golang)    sayHello(php)}Copy the code

Program output:

Hi, I am GO!Hi, I am PHP!Copy the code

In main, when sayHello() is called, golang, PHP objects are passed in. They do not explicitly declare that they implement the IGreeting type, only the sayHello() function specified by the interface. In fact, the compiler implicitly converts a Golang PHP object to IGreeting when it calls sayHello(), which is a type checking feature of static languages.

By the way, a few more features of dynamic languages:

The type of the variable binding is indeterminate, it is only at run time that functions and methods can accept arguments of any type, and it is not necessary to implement an interface when calling without checking parameter types

To summarize, a duck type is a dynamic language style in which an object’s valid semantics are determined not by inheriting from a particular class or implementing a particular interface, but by its “current collection of methods and attributes.” Go is a static language that implements the duck type through the interface, where the Go compiler actually does the implicit conversion.

2. Difference between value receiver and pointer receiver

methods

Method can add new behavior to user-defined types. The difference between a method and a function is that a method has a receiver. Add a receiver to a function and it becomes a method. The receiver can be either a value receiver or a pointer receiver.

When a method is called, the value type can call either the value receiver’s method or the pointer receiver’s method. Pointer types can call either the pointer receiver’s method or the value receiver’s method.

That is, values and Pointers of that type can be called regardless of the recipient of a method, and do not have to conform strictly to the recipient’s type.

Here’s an example:

package mainimport "fmt"type Person struct {    age int}func (p Person) howOld() int {    return p.age}func (p *Person) growUp() {    p.age += 1}func main() {// qcrao is a value type    qcrao := Person{age: 18}// A value type calls a method whose receiver is also a value type    fmt.Println(qcrao.howOld())// A value type calls a method whose receiver is a pointer type    qcrao.growUp()    fmt.Println(qcrao.howOld())    // ----------------------// stefno is a pointer type    stefno := &Person{age: 100}// The pointer type calls a method whose receiver is a value type    fmt.Println(stefno.howOld())// A pointer type calls a method whose receiver is also a pointer type    stefno.growUp()    fmt.Println(stefno.howOld())}Copy the code

The output of the above example is:

1819100101Copy the code

When growUp is called, its Age value changes regardless of whether the caller is of a value type or a pointer type.

In fact, when the receiver types of the type and method are different, the compiler actually does some work behind the scenes, rendering it in a table:

Value of the receiver

Pointer receiver

Value type caller

Method uses a copy of the caller, similar to “pass values”

Call a method using a reference to a value. In the above example, qcrao.growup () is actually (&qcrao.growup ()).

Pointer type caller

Pointers are dereferenced as values. In the above example, stefno. HowOld () is actually (*stefno).howold ().

This is also called “passing”, where operations in a method affect the caller, similar to pointer passing, where a pointer is copied

Value receiver and pointer receiver

As mentioned earlier, regardless of whether the receiver type is a value type or a pointer type, it can be called by either a value type or a pointer type, which actually works through syntactic sugar.

Conclusion: implementing a method whose receiver is a value type automatically implements a method whose receiver is a pointer type. Methods that implement a receiver type of pointer do not automatically generate methods that correspond to a receiver type of value.

Take a look at an example to make it perfectly clear:

package mainimport "fmt"type coder interface {    code()    debug()}type Gopher struct {    language string}func (p Gopher) code() {    fmt.Printf("I am coding %s language\n", p.language)}func (p *Gopher) debug() {    fmt.Printf("I am debuging %s language\n", p.language)}func main() {    var c coder = &Gopher{"Go"}    c.code()    c.debug()}Copy the code

An interface coder is defined in the above code. The interface defines two functions:

code()debug()Copy the code

Then define a structure Gopher, it implements two methods, a value receiver, a pointer receiver.

Finally, we call the two defined functions in main using variables of interface type.

Run it and the result is:

I am coding Go languageI am debuging Go languageCopy the code

But if we change the first statement of main:

func main() {    var c coder = Gopher{"Go"}    c.code()    c.debug()}Copy the code

Run it and report an error:

./main.go:24:6: cannot use Programmer literal (type Programmer) as type coder in assignment:    Programmer does not implement coder (debug method has pointer receiver)Copy the code

See the difference between these two pieces of code? The first time is to assign &gopher to coder; The second time is to assign Gopher to coder.

Gopher does not implement coder. Obviously, the Gopher type doesn’t implement debug methods; On the surface, Gopher types don’t implement code methods either, but because Gopher types implement code methods, they let Gopher types automatically have code methods.

Of course, there is a simple explanation for this: The receiver is a pointer type method, and it is likely that changes to the receiver’s properties will occur in the method, affecting the receiver; For methods where the receiver is a value type, there is no effect on the receiver itself in the method.

So, when you implement a method whose receiver is a value type, you can automatically generate a method whose receiver is a pointer type, because neither affects the receiver. However, when a method whose receiver is a pointer type is implemented, if at this point a method whose receiver is a value type is automatically generated, the expected change to the receiver (implemented through the pointer) cannot be implemented because the value type makes a copy and doesn’t really affect the caller.

Finally, just remember this:

If you implement a method whose receiver is a value type, you implicitly also implement a method whose receiver is a pointer type.

When the two are used

If the receiver of a method is a value type, whether the caller is an object or an object pointer, the modification is a copy of the object and does not affect the caller. If the receiver of a method is a pointer type, the caller modifies the object to which the pointer points.

Reasons to use Pointers as method recipients:

• Method can modify the value pointed to by the receiver.

• Avoid copying the value every time a method is called, which is more efficient when the value type is a large structure.

Whether to use a value receiver or a pointer receiver is not determined by whether the method modifies the caller (that is, the receiver), but should be based on the nature of the type.

If the type is “primitive in nature,” that is, its members are made up of primitive types built into the Go language, such as strings, integer values, etc., then define a method for the value receiver type. The built-in reference types, such as Slice, map, interface, and Channel, are special. When you declare them, you actually create a header, which is also a direct way to define the recipient type of the value. In this way, when you call a function, you copy these types of headers directly, and the headers themselves are designed for copying.

If the type is nonprimitive in nature and cannot be safely copied, and should always be shared, define methods for pointer receivers. For example, struct files in the go source code should not be copied, there should be only one entity.

This paragraph is quite roundabout, you can Go to see “Go Language Practice” 5.3 that section.

3. What is the difference between iFace and eface

Iface and eface are both low-level constructs of Go describing interfaces. The difference is that iFace describes interfaces that contain methods, while eface is an empty interface that contains no methods: interface{}.

Take a look at the source code:

type 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}Copy the code

Iface maintains two Pointers internally, TAB pointing to an ITAB entity that represents the type of the interface and the entity type assigned to the interface. Data points to an interface-specific value, generally a pointer to heap memory.

The _type field describes the type of the entity, including memory alignment, size, and so on. The Inter field describes the type of the interface. The fun field places the method address of the specific data type corresponding to the interface method, implementing dynamic dispatch of the interface calling method, generally updating the table every time the interface assignment is converted, or directly fetching the cached ITAB.

Only methods related to the entity type and interface are listed here; other methods of the entity type are not listed here. If you’ve learned C++, this is an analogy to virtual functions.

Also, you might be wondering why the fun array is 1 in size, but what if the interface defines multiple methods? In fact, the function pointer to the first method is stored here, and if there are more methods, the memory space after it is stored. From an assembly point of view, these function Pointers can be obtained by adding addresses, which doesn’t matter. Incidentally, these methods are sorted in lexicographical order by function names.

Consider the interfaceType type, which describes the type of the interface:

type interfacetype struct {    typ     _type    pkgpath name    mhdr    []imethod}Copy the code

As you can see, it wraps the _type type, which is actually a structure that describes the various data types in the Go language. We notice that there is also an MHDR field that represents the list of functions defined by the interface, and the PKgPATH record defines the package name of the interface.

Here’s an overview of the iFace structure:

Here is the eface source code:

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

Compared to iFace, eFace is relatively simple. Only one _type field is maintained, representing the specific entity type hosted by the empty interface. Data describes specific values.

Let’s look at an example:

package mainimport "fmt"func main() {    x := 200    var any interface{} = x    fmt.Println(any)    g := Gopher{"Go"}    var c coder = g    fmt.Println(c)}type coder interface {    code()    debug()}type Gopher struct {    language string}func (p Gopher) code() {    fmt.Printf("I am coding %s language\n", p.language)}func (p Gopher) debug() {    fmt.Printf("I am debuging %s language\n", p.language)}Copy the code

Execute command to print assembly language:

go tool compile -S ./src/main.goCopy the code

As you can see, two functions are called in main:

func convT2E64(t *_type, elem unsafe.Pointer) (e eface)func convT2I(tab *itab, elem unsafe.Pointer) (i iface)Copy the code

The arguments of the above two functions are related to the fields of the iFace and eface structures: both functions assemble the arguments to form the final interface.

As a final supplement, let’s look at the _type structure:

type _type struct {// Type size    size       uintptr    ptrdata    uintptr// Hash value of type    hash       uint32// Type of flag, related to reflection    tflag      tflag// Memory alignment is related    align      uint8    fieldalign uint8// Type numbers such as bool, slice, struct, etc    kind       uint8    alg        *typeAlg/ / gc    gcdata    *byte    str       nameOff    ptrToThis typeOff}Copy the code

All data types in Go language are managed by adding some additional fields on the basis of the _type field:

type arraytype struct {    typ   _type    elem  *_type    slice *_type    len   uintptr}type chantype struct {    typ  _type    elem *_type    dir  uintptr}type slicetype struct {    typ  _type    elem *_type}type structtype struct {    typ     _type    pkgPath name    fields  []structfield}Copy the code

The structural definition of these data types is the basis for reflection implementation.

4. Dynamic type and value of the interface

Iface contains two fields: TAB is the interface table pointer, pointing to the type information; Data is a data pointer that points to specific data. They are called dynamic types and dynamic values, respectively. Interface values include dynamic types and dynamic values.

Interface types are compared to nil

A zero value for an interface value means that both the dynamic type and dynamic value are nil. The interface value is said to be interface value == nil only if and only if both parts are nil.

Here’s an example:

package mainimport "fmt"type Coder interface {    code()}type Gopher struct {    name string}func (g Gopher) code() {    fmt.Printf("%s is coding\n", g.name)}func main() {    var c Coder    fmt.Println(c == nil)    fmt.Printf("c: %T, %v\n", c, c)    var g *Gopher    fmt.Println(g == nil)    c = g    fmt.Println(c == nil)    fmt.Printf("c: %T, %v\n", c, c)}Copy the code

Output:

truec: <nil>, <nil>truefalsec: *main.Gopher, <nil>Copy the code

When g is assigned to C, the dynamic type of C is *main.Gopher. The dynamic type of C is still nil, but when c is compared to nil, the result is false.

Take a look at the output of an example:

package mainimport "fmt"type MyError struct {}func (i MyError) Error() string {    return "MyError"}func main() {    err := Process()    fmt.Println(err)    fmt.Println(err == nil)}func Process() error {    var err *MyError = nil    return err}Copy the code

Function running result:

<nil>falseCopy the code

Here first defined a MyError structure, the implementation of the Error function, also the implementation of the Error interface. The Process function returns an error interface that implies type conversions. So even though it’s nil, it’s actually of type *MyError, and when you compare it to nil, it’s false.

[Extension 3] How to print the dynamic type and value of the interface?

Look directly at the code:

package mainimport (    "unsafe"    "fmt")type iface struct {    itab, data uintptr}func main() {    var a interface{} = nil    var b interface{} = (*int)(nil)    x := 5    var c interface{} = (*int)(&x)    ia := *(*iface)(unsafe.Pointer(&a))    ib := *(*iface)(unsafe.Pointer(&b))    ic := *(*iface)(unsafe.Pointer(&c))    fmt.Println(ia, ib, ic)    fmt.Println(*(*int)(unsafe.Pointer(ic.data)))}Copy the code

An iFace structure is directly defined in the code, and two Pointers are used to describe ITAB and data. After that, the contents of A, B and C in memory are forced to be interpreted as our customized IFace. Finally, you can print out the address of the dynamic type and dynamic value.

The running results are as follows:

{0 0} {17426912 0} {17426912 842350714568}5Copy the code

The address of both the dynamic type and dynamic value of A is 0, that is, nil; The dynamic type of b is the same as the dynamic type of C. Finally, the dynamic value of C is 5.

5. The compiler automatically detects whether the type implements the interface

It’s common to see open source libraries with strange uses like the following:

var _ io.Writer = (*myWriter)(nil)Copy the code

It’s a bit confusing at this point, not knowing what the author is trying to do, but this is actually the answer to the question. The compiler checks to see if the *myWriter type implements the IO.Writer interface.

Here’s an example:

package mainimport "io"type myWriter struct {}/*func (w myWriter) Write(p []byte) (n int, err error) {    return} * /func main() {// Check whether the *myWriter type implements the IO.Writer interface    var _ io.Writer = (*myWriter)(nil)// Check whether the myWriter type implements the IO.Writer interface    var _ io.Writer = myWriter{}}Copy the code

After commenting out the Write function defined for myWriter, run the program:

src/main.go:14:6: cannot use (*myWriter)(nil) (type *myWriter) as type io.Writer in assignment:    *myWriter does not implement io.Writer (missing Write method)src/main.go:15:6: cannot use myWriter literal (type myWriter) as type io.Writer in assignment:    myWriter does not implement io.Writer (missing Write method)Copy the code

MyWriter /myWriter does not implement the IO.Writer interface, that is, the Write method is not implemented.

After uncomment, run the program without error.

In fact, the assignment is implicitly converted, and during the conversion the compiler checks whether the type to the right of the equals sign implements the function specified by the interface to the left of the equals sign.

To sum up, you can check if a type implements an interface by adding something like the following to your code:

var _ io.Writer = (*myWriter)(nil)var _ io.Writer = myWriter{}Copy the code

6. What is the interface construction process

We have looked at the source code for iface and eface and know that the most important ones for iFace are itab and _type.

In order to understand how interfaces are constructed, I will take up the weapons of assembly and restore the truth behind them.

Take a look at an example code:

package mainimport "fmt"type Person interface {    growUp()}type Student struct {    age int}func (p Student) growUp() {    p.age += 1    return}func main() {    var qcrao = Person(Student{age: 18})    fmt.Println(qcrao)}Copy the code

Execute command:

go tool compile -S ./src/main.goCopy the code

The assembly code for main is as follows:

0x0000 00000 (./src/main.go:30) TEXT    "".main(SB), $80-00x0000 00000 (./src/main.go:30) MOVQ    (TLS), CX0x0009 00009 (./src/main.go:30) CMPQ    SP, 16(CX)0x000d 00013 (./src/main.go:30) JLS     1570x0013 00019 (./src/main.go:30) SUBQ    $80, SP0x0017 00023 (./src/main.go:30) MOVQ    BP, 72(SP)0x001c 00028 (./src/main.go:30) LEAQ    72(SP), BP0 x0021 00033 (. / SRC/main. Go: 30) FUNCDATA $0, gclocals · 69 c1753bd5f81501d95132d08af04464 (SB.)0 x0021 00033 (. / SRC/main. Go: 30) FUNCDATA $1, gclocals · e226d4ae4a7cad8835311c6a4683c14f (SB.)0x0021 00033 (./src/main.go:31) MOVQ $18, "".. autotmp_1+48(SP)0x002a 00042 (./src/main.go:31) LEAQ    go.itab."".Student,"".Person(SB), AX0x0031 00049 (./src/main.go:31) MOVQ    AX, (SP)0x0035 00053 (./src/main.go:31) LEAQ "".. autotmp_1+48(SP), AX0x003a 00058 (./src/main.go:31) MOVQ    AX, 8(SP)0x003f 00063 (./src/main.go:31) PCDATA  $0, $00x003f 00063 (./src/main.go:31) CALL    runtime.convT2I64(SB)0x0044 00068 (./src/main.go:31) MOVQ    24(SP), AX0x0049 00073 (./src/main.go:31) MOVQ    16(SP), CX0x004e 00078 (./src/main.go:33) TESTQ   CX, CX0x0051 00081 (./src/main.go:33) JEQ     870x0053 00083 (./src/main.go:33) MOVQ    8(CX), CX0x0057 00087 (./src/main.go:33) MOVQ $0, "".. autotmp_2+56(SP)0x0060 00096 (./src/main.go:33) MOVQ $0, "".. autotmp_2+64(SP)0x0069 00105 (./src/main.go:33) MOVQ CX, "".. autotmp_2+56(SP)0x006e 00110 (./src/main.go:33) MOVQ AX, "".. autotmp_2+64(SP)0x0073 00115 (./src/main.go:33) LEAQ "".. autotmp_2+56(SP), AX0x0078 00120 (./src/main.go:33) MOVQ    AX, (SP)0x007c 00124 (./src/main.go:33) MOVQ    $1, 8(SP)0x0085 00133 (./src/main.go:33) MOVQ    $1, 16(SP)0x008e 00142 (./src/main.go:33) PCDATA  $0, $10x008e 00142 (./src/main.go:33) CALL    fmt.Println(SB)0x0093 00147 (./src/main.go:34) MOVQ    72(SP), BP0x0098 00152 (./src/main.go:34) ADDQ    $80, SP0x009c 00156 (./src/main.go:34) RET0x009d 00157 (./src/main.go:34) NOP0x009d 00157 (./src/main.go:30) PCDATA  $0, $-10x009d 00157 (./src/main.go:30) CALL    runtime.morestack_noctxt(SB)0x00a2 00162 (./src/main.go:30) JMP     0Copy the code

Let’s start at line 10. If you don’t understand the first few lines of assembly code, you can go back to the first two articles of the public account, which I omit here.

Assembly lines

operation

10-14

Construct arguments to the call runtime.convt2i64 (SB)

Let’s look at the argument form of this function:

func convT2I64(tab *itab, elem unsafe.Pointer) (i iface) {/ /...}Copy the code

ConvT2I64 constructs an Inteface, our Person interface.

The location of the first argument is (SP), which is assigned the address of go.itab.””.student,””.person (SB).

From the generated assembly we find:

go.itab."".Student,"".Person SNOPTRDATA dupok size=40        0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00          0x0010 00 00 00 00 00 00 00 00 da 9f 20 d4                      rel 0+8 t=1 type."".Person+0        rel 8+8 t=1 type."".Student+0Copy the code

Size =40 Size =40 bytes, to recap:

type itab struct {Inter * interfaceType // 8 bytes_type *_type // 8 bytesLink *itab // 8 bytesHash uint32 // 4 bytesBad bool // 1 byteInhash bool // 1 byteUnused [2]byte // 2 bytesFun [1] Uintptr // variable sized // 8 bytes}Copy the code

Adding up the size of each field gives the ITAB structure a size of 40 bytes. Note that most of the numbers are zeros. The first four bytes da 9f 20 d4 are actually itab hash values, which are used to determine if the two types are the same.

The next two lines are linked instructions, which simply combine all the source files and assign each symbol a global location value. The first 8 bytes store the address of type.””.Person, which corresponds to the INTER field in itAB, indicating the interface type. Type.””.Student address, corresponding to the itab _type field, indicates the specific type.

The second argument is simpler, and is the address of the number 18, which is used to initialize the Student structure.

Assembly lines

operation

15

Call the runtime. ConvT2I64 (SB)

Take a look at the code:

func convT2I64(tab *itab, elem unsafe.Pointer) (i iface) {    t := tab._type/ /...    var x unsafe.Pointer    if *(*uint64)(elem) == 0 {        x = unsafe.Pointer(&zeroVal[0])    } else {        x = mallocgc(8, t, false)        *(*uint64)(x) = *(*uint64)(elem)    }    i.tab = tab    i.data = x    return}Copy the code

This code is relatively simple, assigning TAB to the TAB field of iFace; The data section allocates a chunk of memory on the heap and copies the 18 that elem points to. And then the iFace is assembled.

Assembly lines

operation

17

Assign i. TAB to CX

18

Assign i. ata to AX

19-21

Check if i.tabb is nil. If not, move CX by 8 bytes. That is, assign the _type field of ITab to CX, which is also the entity type of the interface, and will eventually be used as an argument to ftt. Println

After that, we will call the FMT.Println function and prepare the parameters.

This completes the construction of an interface.

How do I print the Hash value of the interface type?

Here is a reference to cao Dashen translated an article, reference materials will be written. Specific practices are as follows:

type iface struct {    tab  *itab    data unsafe.Pointer}type itab struct {    inter uintptr    _type uintptr    link uintptr    hash  uint32    _     [4]byte    fun   [1]uintptr}func main() {    var qcrao = Person(Student{age: 18})    iface := (*iface)(unsafe.Pointer(&qcrao))    fmt.Printf("iface.tab.hash = %#x\n", iface.tab.hash)}Copy the code

A copycat version of iFace and ITAB is called copycat because some key data structures in ITAB are not specified, such as _type, which can be found by comparing the real definition, but the copycat version still works because _type is just a pointer.

In main, we construct an interface object, qcrao, cast it, and then read the hash value. You can try it yourself.

Running results:

iface.tab.hash = 0xd4209fdaCopy the code

It is worth mentioning that when CONSTRUCTING the interface Qcrao, even if I write age as something else, the hash value is still the same, which should be expected. The hash value is only related to its fields and methods.

7. The difference between casts and assertions

We know that implicit type conversions are not allowed in Go, which means you can’t have variables of different types on both sides of =.

Type conversions and type assertions are essentially the conversion of one type to another. The difference is that type assertions are operations on interface variables.

Type conversion

For type conversions, the two types must be compatible. The syntax for type conversions is:

< result type > := < target type > (< expression >)

package mainimport "fmt"func main() {    var i int = 9    var f float64    f = float64(i)    fmt.Printf("%T, %v\n", f, f)F = 10.8,    a := int(f)    fmt.Printf("%T, %v\n", a, a)    // s := []int(i)Copy the code

In the above code, I defined a variable of int and float64 and tried to convert them to each other. The result was successful: int and float64 are compatible.

If I comment out the last line of code, the compiler reports a type incompatibility error:

cannot convert i (type int) to type []intCopy the code

assertions

As mentioned earlier, because the empty interface{} does not define any functions, all types in Go implement empty interfaces. When a function parameter is interface{}, you need to assert the parameter in the function to get its true type.

The syntax for assertions is:

// Secure type assertion

< value of target type >, < Boolean parameter > := < expression >.(Target type)

// Insecure type assertion

< value of target type > := < expression >.(Target type)

Conversion is similar to type assertion, except that type assertion is an operation on an interface.

Here’s a quick example:

package mainimport "fmt"type Student struct {    Name string    Age int}func main() {    var i interface{} = new(Student)    s := i.(Student)    fmt.Println(s)}Copy the code

Run it:

panic: interface conversion: interface {} is *main.Student, not main.StudentCopy the code

The assertion fails because I is *Student, not Student. Here panic occurs directly, and the online code may not be suitable for this, you can use the “security assertion” syntax:

func main() {    var i interface{} = new(Student)    s, ok := i.(Student)    if ok {        fmt.Println(s)    }}Copy the code

This way, there will be no panic even if the assertion fails.

Another form of assertion is used to determine the type of interface using switch statements. Each case is considered sequentially. When a case is hit, the statements in the case are executed, so the order of the case statements is important because it is likely that there will be multiple case matches.

A code example is as follows:

func main() {    //var i interface{} = new(Student)    //var i interface{} = (*Student)(nil)    var i interface{}    fmt.Printf("%p %v\n", &i, i)    judge(i)}func judge(v interface{}) {    fmt.Printf("%p %v\n", &v, v)    switch v := v.(type) {    case nil:        fmt.Printf("%p %v\n", &v, v)        fmt.Printf("nil type[%T] %v\n", v, v)    case Student:        fmt.Printf("%p %v\n", &v, v)        fmt.Printf("Student type[%T] %v\n", v, v)    case *Student:        fmt.Printf("%p %v\n", &v, v)        fmt.Printf("*Student type[%T] %v\n", v, v)    default:        fmt.Printf("%p %v\n", &v, v)        fmt.Printf("unknow\n")    }}type Student struct {    Name string    Age int}Copy the code

The main function has three different lines of declarations. Run one line at a time and comment the other two lines to get three sets of results:

// --- var i interface{} = new(Student)0xc4200701b0 [Name: ], [Age: 0]0xc4200701d0 [Name: ], [Age: 0]0xc420080020 [Name: ], [Age: 0]*Student type[*main.Student] [Name: ], [Age: 0]// --- var i interface{} = (*Student)(nil)0xc42000e1d0 <nil>0xc42000e1f0 <nil>0xc42000c030 <nil>*Student type[*main.Student] <nil>// --- var i interface{}0xc42000e1d0 <nil>0xc42000e1e0 <nil>0xc42000e1f0 <nil>nil type[<nil>] <nil>Copy the code

For the first line:

var i interface{} = new(Student)Copy the code

I is of type *Student and matches the third case. From the three addresses printed, the variables in all three are actually different. There’s a local variable I in main; When YOU call a function, you’re actually copying a copy of the arguments, so you have another variable v in the function, which is a copy of I; After assertion, a new copy is made. So the addresses of the three variables that are printed are all different.

For the second line:

var i interface{} = (*Student)(nil)Copy the code

So the point here is that I is dynamically typed (*Student), it’s nil, it’s not of type nil, and when you compare it to nil, you get false.

The last line of statements:

var i interface{}Copy the code

This time I is nil.

FMT.Println takes the parameter interface. For built-in types, the function uses internal enumeration to derive its true type, which is then converted to string printing. For custom types, we first determine whether the type implements the String() method. If so, we print the result of the String() method directly. Otherwise, it prints by iterating through the members of the object through reflection.

Let’s take a quick example. It’s easy, don’t get nervous:

package mainimport "fmt"type Student struct {    Name string    Age int}func main() {    var s = Student{        Name: "qcrao",        Age: 18,    }    fmt.Println(s)}Copy the code

Since the Student structure does not implement the String() method, fmt.println prints member variables one by one using reflection:

{qcrao 18}Copy the code

Add an implementation of the String() method:

func (s Student) String() string {    return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)}Copy the code

Print result:

[Name: qcrao], [Age: 18]Copy the code

According to our custom method to print.

【 参 考 译 文 2】 For the above example, if you change it:

func (s *Student) String() string {    return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)}Copy the code

The Student structure now has only one String() function whose receiver type is pointer, printing the result:

{qcrao 18}Copy the code

Why is that?

Methods of type T where only the receiver is T; While type *T has methods whose recipients are T and *T. The syntactic way in which T can be modulated directly to *T is just the syntactic sugar of Go.

So, when the Student structure defines a String() method whose recipient type is a value type, it passes

fmt.Println(s)fmt.Println(&s)Copy the code

Can be printed in a custom format.

If the Student structure defines a String() method whose recipient type is a pointer, it will only pass

fmt.Println(&s)Copy the code

To print in a custom format.

8. Principle of interface conversion

As you can see from the aforementioned iFace source code, it actually contains the type of the interface, InterfaceType, and the type _type of the entity type, both of which are members of the IFace field ITab. That is, generating an ITAB requires both the interface type and the entity type.

<interface type, entity type > ->itable

When determining whether a type satisfies an interface, Go matches the method set of the type with the method set required by the interface. If the method set of the type completely contains the method set of the interface, the type can be considered to implement the interface.

For example, if a certain type has M methods and an interface has N methods, it is easy to know that the time complexity of this judgment is O(Mn). Go will sort the functions of the method set according to the lexicographical order of function names, so the actual time complexity is O(m+ N).

Here we explore the principles behind converting one interface to another, of course, because of type compatibility.

Let’s just look at an example:

package mainimport "fmt"type coder interface {    code()    run()}type runner interface {    run()}type Gopher struct {    language string}func (g Gopher) code() {    return}func (g Gopher) run() {    return}func main() {    var c coder = Gopher{}    var r runner    r = c    fmt.Println(c, r)}Copy the code

A quick explanation of this code: Two interfaces are defined: coder and runner. An entity type Gopher is defined, which implements two methods, run() and code(). The main function defines an interface variable c, binds to a Gopher object, and assigns C to another interface variable R. The assignment succeeds because c contains the run() method. Thus, the two interface variables complete the transformation.

Execute command:

go tool compile -S ./src/main.goCopy the code

Get the assembly command for the main function, you can see: Call runtime.convI2I(SB); convI2I(SB); convI2I(SB);

func convI2I(inter *interfacetype, i iface) (r iface) {    tab := i.tab    if tab == nil {        return    }    if tab.inter == inter {        r.tab = tab        r.data = i.data        return    }    r.tab = getitab(inter, tab._type, false)    r.data = i.data    return}Copy the code

The code is relatively simple. The function parameters inter represent the interface type, I represent the interface bound to the entity type, and r represent the new iFace after the interface transformation. Iface consists of two fields: TAB and data. So, in fact, all convI2I really needs to do is find the TAB and data for the new interface, and that’s it.

We also know that TAB is made up of interfacetype interfacetype and entity type _type. So the most critical statement is r.tabb = getitab(inter, tab._type, false).

Therefore, focus on the source of the getitab function, just look at the key points:

func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {/ /...// Compute the hash value based on inter and TYp    h := itabhash(inter, typ)    // look twice - once without lock, once with.    // common case will be no lock contention.    var m *itab    var locked int    for locked = 0; locked < 2; locked++ {if locked ! = 0 {            lock(&ifaceLock)        }// Iterate over a slot in the hash tablefor m = (*itab)(atomic.Loadp(unsafe.Pointer(&hash[h]))); m ! = nil; m = m.link {// If itab has been found in the hash table (both inter and TYp Pointers are the same)            if m.inter == inter && m._type == typ {/ /...if locked ! = 0 {                    unlock(&ifaceLock)                }                return m            }        }    }// If no ITab is found in the hash table, create a new ITab    m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys))    m.inter = inter    m._type = typ// Add to the global hash table    additab(m, true, canfail)    unlock(&ifaceLock)    if m.bad {        return nil    }    return m}Copy the code

To summarize, the getitab function looks for the global ITAB hash table based on the interfaceType and _type, and returns the global ITAB hash table if found. Otherwise, a new ITAB is generated based on the given interfaceType and _type, and inserted into the ITAB hash table, so that the next time the ITAB can be retrieved directly.

I did it twice, and I locked it the second time, because if I didn’t find it the first time, if I still didn’t find itAB the second time, I need to generate a new one and write to the hash table, so I need to lock it. This way, if other coroutines look for the same ITAB and don’t find it, the second lookup will be hung, and after that, the itAB that the first coroutine wrote to the hash table will be found.

Look at the code for the additab function again:

// Check that the _type matches the interface_type and create the itab structure to put it in the hash tablefunc additab(m *itab, locked, canfail bool) {    inter := m.inter    typ := m._type    x := typ.uncommon()    // both inter and typ have method sorted by name,    // and interface names are unique,    // so can iterate over both in lock step;    // the loop is O(ni+nt) not O(ni*nt).    // // The inter and TYP methods are sorted by method name// And method names are unique. So the number of cycles is fixed// Just loop O(ni+nt) instead of O(ni*nt)    ni := len(inter.mhdr)    nt := int(x.mcount)    xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]    j := 0    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)// Check whether the method name is consistent            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 {// Get the function address and add it to the itab.fun array                        ifn := typ.textOff(t.ifn)                        *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn                    }                    goto nextimethod                }            }        }/ /...        m.bad = true        break    nextimethod:    }if ! locked {        throw("invalid itab locking")    }// Computes the hash value    h := itabhash(inter, typ)// Add to the Hash Slot list    m.link = hash[h]    m.inhash = true    atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))}Copy the code

Additab checks whether the interfaceType held by itAB matches the _type, that is, whether the _type fully implements the interfaceType method. So if you look at the method lists of the two the overlap is the method list that interfaceType holds. Noticed that one of the double loop, at first glance, cycles is ni * nt, but as a result of the function list is sorted by the function name, so only perform the ni + nt times, the code by a tip: the second loop is not starting from zero, but from the last position of the traverse to the beginning.

Finding the hash function is simpler:

func itabhash(inter *interfacetype, typ *_type) uint32 {    h := inter.typ.hash    h += 17 * typ.hash    return h % hashSize}Copy the code

The value of hashSize is 1009.

More generally, conV family functions are called when an entity type is assigned to an interface, such as convT2E to a null interface and convT2I to a non-null interface. These functions are similar:

1. When the interface type is empty, the _type field directly copies the _type of the source type. Call mallocGC to get a new piece of memory, copy the values into it, and data points to the new memory.

2. When the specific type is converted to a non-empty interface, the input parameter TAB is pre-generated by the compiler during compilation. The TAB field of the new interface directly points to the ITAB that TAB points to. Call mallocGC to get a new piece of memory, copy the values into it, and data points to the new memory.

3. For the interface forwarding interface, ITAB calls getitab to obtain it. It is generated only once and then retrieved directly from the hash table.

9. How to use interface to implement polymorphism

The Go language does not design concepts such as virtual functions, pure virtual functions, inheritance, multiple inheritance, etc., but it supports object-oriented features elegantly through interfaces.

Polymorphism is a run-time behavior that has the following characteristics:

1. One type has multiple types of capabilities

2. Allow different objects to react flexibly to the same message

3. Treat all objects in a generic way

4. Non-dynamic languages must be implemented through inheritance and interfaces

Look at an example of code that implements polymorphism:

package mainimport "fmt"func main() {    qcrao := Student{age: 18}    whatJob(&qcrao)    growUp(&qcrao)    fmt.Println(qcrao)    stefno := Programmer{age: 100}    whatJob(stefno)    growUp(stefno)    fmt.Println(stefno)}func whatJob(p Person) {    p.job()}func growUp(p Person) {    p.growUp()}type Person interface {    job()    growUp()}type Student struct {    age int}func (p Student) job() {    fmt.Println("I am a student.")    return}func (p *Student) growUp() {    p.age += 1    return}type Programmer struct {    age int}func (p Programmer) job() {    fmt.Println("I am a programmer.")    return}func (p Programmer) growUp() {// Programmers grow old too fast    p.age += 10    return}Copy the code

This code defines a Person interface that contains two functions:

job()growUp()Copy the code

We then define two more constructs, Student and Programmer, while the types *Student and Programmer implement the two functions defined by the Person interface. Note that the *Student type implements the interface, but the Student type does not.

After that, I’ve defined two functions whose parameters are the Person interface:

func whatJob(p Person)func growUp(p Person)Copy the code

The main function becomes the Student and Programmer objects, which are passed to the whatJob and growUp functions, respectively. Function, the interface function is called directly, and the actual execution is based on what type of entity is passed in, calling the function implemented by the entity type. Thus, different objects can have multiple representations of the same message, and polymorphism is realized.

To dig a little deeper, inside the function whatJob() or growUp(), the interface person binds the entity type *Student or Programmer. Fun [0] : s.tabb ->fun[0] : s.tabb ->fun[0] : s.tabb ->fun[0] : s.tabb ->fun[0] : s.tabb ->fun[0] : s.tabb ->fun[0]

Run the code:

I am a student.{19}I am a programmer.{100}Copy the code

10. What are the similarities and differences between Go interface and C++ interface

An interface defines a specification that describes the behavior and functionality of a class without implementing it.

C++ interfaces are implemented using abstract classes. A class is abstract if at least one of its functions is declared pure virtual. Pure virtual functions are specified by using “= 0” in the declaration. Such as:

class Shape{   public:// A pure virtual function      virtual double getArea() = 0;   private:string name; / / name};Copy the code

Abstract classes are designed to provide an appropriate base class from which other classes can inherit. An abstract class cannot be used to instantiate an object; it can only be used as an interface.

A derived class needs to explicitly declare that it inherits from the base class, and it needs to implement all pure virtual functions in the base class.

The way C++ defines an interface is called “intrusive,” while Go takes the “non-intrusive” approach. It does not require an explicit declaration, but simply implements the functions defined by the interface, which the compiler automatically recognizes.

The differences in the way C++ and Go define interfaces also lead to differences in the underlying implementation. C++ uses virtual function tables to enable base classes to call functions of derived classes. Go uses the FUN field in ITAB to implement interface variables that call entity-type functions. Virtual function tables in C++ are generated at compile time; The FUN field in Go’s ITAB is dynamically generated at run time. The reason for this is that entity types in Go may inadvertently implement N interfaces, many of which are not necessarily needed, and therefore cannot generate an ITAB for all interfaces implemented by the type. This is also the effect of “non-invasive”. This does not exist in C++ because derivation needs to be explicitly declared from which base class it inherits.

The resources

https://zhuanlan.zhihu.com/p/27055513 contains reflection, such as interface source code analysis 】 【

The difference between the virtual function table and c + + 】 mp.weixin.qq.com/s/jU9HeR1tO…

The concrete types to meet KouFu value 】 https://tiancaiamao.gitbooks.io/go-internals/content/zh/07.2.html

“Go night reading group discussion” https://github.com/developer-learning/reading-go/blob/master/content/discuss/2018-08-30-understanding-go-inter faces.md

【 Liao Xuefeng The duck type] https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431865288798deef438d865e4c29 85acff7e9fad15e3000

The value type and pointer types, iface source 】 https://www.jianshu.com/p/5f8ecbe4f6af

Overall description itab generation, function 】 【 http://www.codeceo.com/article/go-interface.html

Series of conv function role 】 【 https://blog.csdn.net/zhonglinzhang/article/details/85772336

【 convI2I itab role 】 https://www.jianshu.com/p/a5e99b1d50b1

【 interface source code interpretation is very good Contains reflection] http://wudaijun.com/2018/01/go-interface-implement/

【 what according to how train of thought to write interface] http://legendtkl.com/2017/06/12/understanding-golang-interface/

[there] assembly analysis, good at http://legendtkl.com/2017/07/01/golang-interface-implement/

The first picture you can refer to GDB debugging 】 【 https://www.do1618.com/archives/797/golang-interface%E5%88%86%E6%9E%90/

The type conversion and assertions 】 https://my.oschina.net/goal/blog/194308

Interface and nil 】 【 https://my.oschina.net/goal/blog/194233

【 functions and methods 】 https://www.jianshu.com/p/5376e15966b3

“Reflection” https://flycode.co/archives/267357

[interface features list] https://segmentfault.com/a/1190000011451232

【 interface comprehensive introduction, contains a c + + contrast 】 https://www.jianshu.com/p/b38b1719636e

【 Go medallion 42 interface] https://github.com/ffhelicopter/Go42/blob/master/content/42_19_interface.md

There are said to Go interface of the definition of the interface] http://blog.zhaojie.me/2013/04/why-i-dont-like-go-style-interface-or-structural-typing.html

“Gopher interface” http://fuxiaohei.me/2017/4/22/gopherchina-2017.html

The translation is good 】 【 mp.weixin.qq.com/s/tBg8D1qXH…

【 】 infoQ article https://www.infoq.cn/article/go-interface-talk

[Go interface explanation] https://zhuanlan.zhihu.com/p/27055513

[Go interface] https://sanyuesha.com/2017/07/22/how-to-understand-go-interface/

【 getitab source description 】 https://www.twblogs.net/a/5c245d59bd9eee16b3db561d

解釋 Golang 中的 Interface 到底是什麼

【 golang IO package use 】 https://www.jianshu.com/p/8c33f7c84509

【 explore c + + as the underlying implementation of interface with the Go to https://www.jianshu.com/p/073c09a05da7 https://github.com/teh-cmc/go-internals/blob/master/chapter2_interfaces/README.md

[Compilation level] http://xargin.com/go-and-interface/

https://i6448038.github.io/2018/10/01/Golang-interface/ has a figure 】 【

Figure 】 【 mp.weixin.qq.com/s/px9BRQrTC…

[English open source book] https://github.com/cch123/go-internals/blob/master/chapter2_interfaces/README.md

http://xargin.com/go-and-interface/