preface

I think, for you to use object-oriented programming programmers, the term “interface” must be familiar, such as Java interface and c++ virtual base class are the implementation of the interface. But the interface concept in Golang is different from other languages and has its own special features, which we will decipher below.

define

The interface in Go is the signature of a set of methods, which is an important part of the Go language. Simply put, an interface is a combination of Method signatures that define the behavior of an object. Interface is a type defined as follows:

type Person interface {
    Eat(food string)}Copy the code

It is defined using the type keyword. More specifically, an interface is a type with a set of methods that define the behavior of the interface. Golang interface definitions cannot contain variables, but are allowed without any methods. This type of interface is called empty Interface.

If a type implements oneinterfaceWe can say that the type implements thatinterfaceSo we implemented all of our typesempty interfaceBecause any type implements at least 0 methods. andgoDon’t likejavaThat requires an explicit keywordinterface, just need to implementinterfaceContains the method.

Implementing an interface

Let’s take the Java language as an example. In Java, to implement an interface, we need to declare:

public class MyWriter implments io.Writer{}
Copy the code

This means that the implementation of the interface needs to be declared, there are dependency restrictions in code writing, and package dependencies need to be dealt with. However, interface implementation in Go language is implicit, for example:

type error interface {
	Error() string
}
type RPCError struct {
	Code    int64
	Message string
}

func (e *RPCError) Error(a) string {
	return fmt.Sprintf("%s, code=%d", e.Message, e.Code)
}
Copy the code

In the above code, there is no shadow of the error interface, we just need to implement the error () string method to implement the error interface. In Go, all the methods that implement the interface implicitly implement the interface. When we use the RPCError structure above, we don’t care what interface it implements. Go only checks whether a type implements an interface when passing, returning, and assigning values to variables.

This is a convenient way to write Go without introducing package dependencies. However, the low-level implementation of interface will introduce some problems when dynamic detection:

  • Performance deteriorates. Using interface as a function parameter, the behavior is determined dynamically at Runtime. Using a concrete type determines the type at compile time.
  • It is not clear which interfaces the struct implements, requiring the use of an IDE or other tool.

Two kinds of interface

Most of you who are just starting out here will be wondering why there are two interfaces, because in Go there are two representations of interfaces. Runtime. iface represents the first interface, the one we implemented above, in which methods are defined; Runtime.eface represents the second interface that does not contain any methods. The second interface is often used in our daily development, so the implementation uses a special type. From a compilation standpoint, Golang does not support generic programming. But you can still use interface{} instead of parameters to implement generics.

Interface internal structure

The Go language divides interface types into two categories based on whether they contain a set of methods:

  • Use the Runtime. iface structure to represent the interface that contains the method
  • useruntime.efaceStruct means that does not contain any methodsinterface{}Type;

The runtime.iface structure is defined in the Go language as follows:

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

There are only two Pointers to the underlying data and type. From this type we can also infer that any type of the Go language can be converted to interface.

Another structure used to represent the interface is Runtime. iface, which has Pointers to the raw data, data, but more important is the TAB field of type Runtime. itab.

type iface struct { / / 16 bytes
	tab  *itab
	data unsafe.Pointer
}
Copy the code

Let’s take a look at the two types of interface:

  • runtime_type

Runtime_type is a runtime representation of the Go language type. Below are structures in the runtime package that contain meta information about many types, such as type size, hash, alignment, and type.

type _type struct {
	size       uintptr
	ptrdata    uintptr
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	equal      func(unsafe.Pointer, unsafe.Pointer) bool
	gcdata     *byte
	str        nameOff
	ptrToThis  typeOff
}
Copy the code

I’ll cover only a few of the more important fields:

  • The size field stores the memory space occupied by the type and provides information about the allocation of memory space.

  • The hash field helps us quickly determine if the types are equal;

  • The equal field, which determines whether multiple objects of the current type are equal, was migrated from the typeAlg structure to reduce the Go language binary package size);

  • runtime_itab

Runtime. itab is a core component of the interface type. Each Runtime. itab takes 32 bytes.

type itab struct { / / 32 bytes
	inter *interfacetype
	_type *_type
	hash  uint32
	_     [4]byte
	fun   [1]uintptr
}
Copy the code

“Inter” and “_type” are fields used to indicate types. “hash” is a copy of “_type.” This field can be used to quickly determine whether the target type is consistent with runtime. Fun is a dynamically sized array that is a virtual function table for dynamic distribution, storing a set of function Pointers. Although the variable is declared as a fixed-size array, the raw pointer is used to retrieve the data, so the number of elements held in the FUN array is indeterminate;

Make a brief introduction of the internal structure, interested students can learn by themselves.

The empty interface (runtime.eface)

Now that we’ve seen what an empty interface is, let’s take a look at how an empty interface is used. Define the function as follows:

func doSomething(v interface{}){}Copy the code

This function takes interface as an input parameter. Note that interface is not an arbitrary type, unlike void * in C. If we convert the type to interface{}, the variable’s type changes at runtime. When you get the variable type, you get interface{}. The reason a function can accept any type is that any type passed to the function when GO is executed is automatically converted to interface{}.

An interface{} slice can accept any type of slice, since an empty interface can accept any type of slice. Let’s give it a try:


import (
	"fmt"
)

func printStr(str []interface{}) {
	for _, val := range str {
		fmt.Println(val)
	}
}

func main(a){
	names := []string{"stanley"."david"."oscar"}
	printStr(names)
}
Copy the code

/main.go:15:10: Cannot use names (type []string) as type []interface {} in argument to printStr.

I am also very confused here. Why didn’t Go help us to automatically convert slice into interface type slice? We tried to use this method in our previous project, but we failed. Later, I finally found the answer. If you are interested, you can read the original text. Here is a brief summary: Interface will occupy two words of storage space, one is its own methods data, and the other is a pointer to its stored value, that is, the value stored by the interface variable, so the length of slice []interface{} is fixed N*2. But the length of []T is N*sizeof(T), and the actual stored values of the two slices are different.

Since this approach doesn’t work, what can be done about it? We can simply use a slice of an element whose type is interface.

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
	interfaceSlice[i] = d
}
Copy the code

Is not emptyinterface

When the Go language implements interfaces, it can have structure-type methods as well as methods that use pointer types. There are no strict rules in the Go language about whether implementors should use a value type or a pointer, so let’s guess what would happen if we used both value type and pointer type methods to implement an interface?

Let’s start with an example:

package main

import (
	"fmt"
)

type Person interface {
	GetAge () int
	SetAge (int)}type Man struct {
	Name string
	Age int
}

func(s Man) GetAge(a)int {
return s.Age
}

func(s *Man) SetAge(age int) {
	s.Age = age
}


func f(p Person){
	p.SetAge(10)
	fmt.Println(p.GetAge())
}

func main(a) {
	p := Man{}
	f(&p) 
}
Copy the code

If you look at the code above, do you have any questions about the input parameter in f(&p)? What if you don’t take the address and just send it? /main.go:34:3: Cannot use p (type Man) as type Person in argument to f: Man does not implement Person (SetAge Method has pointer receiver). Since the receiver of SetAge method is a pointer type, f is passed a copy of P. In the conversion of P to person, the copy of P is a pointer type that does not satisfy the SetAge method. And that’s one of the things that’s going on in go is that functions are passed by value.

The above example causes this problem because of value passing. In fact, regardless of whether the receiver type is a value type or a pointer type, it can be called by a value type or a pointer type, which actually works through syntax sugar. Implementing a method whose receiver is a value type is equivalent to automatically implementing 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.

Here’s an example:

type Animal interface {
	Walk()
	Eat()
}


type Dog struct {
	Name string
}

func (d *Dog)Walk(a)  {
	fmt.Println("go")}func (d *Dog)Eat(a)  {
	fmt.Println("eat shit")}func main(a) {
	var d Animal = &Dog{"nene"}
	d.Eat()
	d.Walk()
}
Copy the code

This interface defines two functions: Animal

Walk()
Eat()
Copy the code

We then define a structure, Dog, that implements two methods, a value receiver and a pointer receiver. There is no problem if we call the two functions defined using variables of interface type. What if we call them like this:

func main(a) {
	var d Animal = Dog{"nene"}
	d.Eat()
	d.Walk()
}
Copy the code

This is an immediate error, we only changed part of it, the first time &dog {“nene”} was assigned to d; The second time, Dog{“nene”} is assigned to d. The second error is because D didn’t implement Animal. This explains the above conclusion, 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.

In summary: If you implement methods whose receivers are of value type, you implicitly implement methods whose receivers are of pointer type.

Types of assertions

Value,ok := em.(T) := em.(T) := em. Em is a variable of type interface, T represents the type to be asserted, value is the value stored in the interface variable, and OK is a bool indicating whether it is of type T for the assertion. The syntax is summarized as follows:

< value of target type >, < Boolean parameter > := < expression >.(Target type)// Secure type assertion< value of target type > := < expression >.(Target type)// Insecure type assertion
Copy the code

Here’s a simple example:

type Dog struct {
	Name string
}

func main(a) {
	var d interface{} = new(Dog)
	d1,ok := d.(Dog)
	if! ok{return
	}
	fmt.Println(d1)
}
Copy the code

This is a safe type assertion and is more suitable for online code. What if you use an unsafe type assertion?

type Dog struct {
	Name string
}

func main(a) {
	var d interface{} = new(Dog)
	d1 := d.(Dog)
	fmt.Println(d1)
}
Copy the code

The following error occurs:

panic: interface conversion: interface {} is *main.Dog, not main.Dog
Copy the code

Assertion failed. Panic occurs directly here, so it is not recommended for online code.

FMT. Println uses type assertions inside the FMT. Println.

The problem

The above introduces the basic use of interface and some possible problems. Here are three questions to see if you have really mastered it.

Problem a

Which line of the following code has a compilation error? (pops)

type Student struct{}func Set(x interface{}){}func Get(x *interface{}){}func main(a) {
	s := Student{}
	p := &s
	// A B C D
	Set(s)
	Get(s)
	Set(p)
	Get(p)
}
Copy the code

A, B, C, D, *interface{} *interface{} *interface{} *interface{} *interface{}

Question 2

What is the result of running this code?

func PrintInterface(val interface{}) {
	if val == nil {
		fmt.Println("this is empty interface")
		return
	}
	fmt.Println("this is non-empty interface")}func main(a) {
	var pointer *string = nil
	PrintInterface(pointer)
}
Copy the code

Answer: This is non-empty interface. Interface {} is an empty interface type.

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

So an implicit conversion occurs when the function PrintInterface is called. In addition to passing parameters to the method, the assignment of variables also triggers an implicit conversion. Eface struct{} is not nil, but the poniter that data points to is nil.

Question 3

What is the result of running this code?


type Animal interface {
	Walk()
}

type Dog struct{}

func (d *Dog) Walk(a) {
	fmt.Println("walk")}func NewAnimal(a) Animal {
	var d *Dog
	return d
}

func main(a) {
	if NewAnimal() == nil {
		fmt.Println("this is empty interface")}else {
		fmt.Println("this is non-empty interface")}}Copy the code

Answer: This is a non-empty interface. The interface is a non-empty iface interface.

type iface struct { / / 16 bytes
	tab  *itab
	data unsafe.Pointer
}
Copy the code

Animal interface{} is a null pointer to nil, but return d will trigger a copy of Animal = p. P is nil, just data in iFace is nil. But iface struct{} itself is not nil.

conclusion

Interface is often used in our daily development, so it is necessary to learn it well. I hope this article can give you a new understanding of the interface of Go language. This article ends here, and we will see you next time ~ ~ ~.

Quality three even (share, praise, look) are the author continue to create more quality content motivation!

Set up a Golang communication group, welcome everyone to join, the first time to watch quality articles, not to be missed oh (public account access)

At the end, I will send you a small welfare. Recently, I was reading the book [micro-service architecture design mode], which is very good. I also collected a PDF, which can be downloaded by myself if you need it. Access: Follow the public account: [Golang Dreamworks], background reply: [micro service], can be obtained.

I have translated a GIN Chinese document, which will be maintained regularly. If you need it, you can download it by replying to [GIN] in the background.

Translated a Machinery Chinese document, will be regularly maintained, there is a need for friends to respond to the background [Machinery] can be obtained.

I am Asong, an ordinary program ape, let GI gradually become stronger together. I built my owngolangCommunication group, you need to add mevxI’ll pull you into the group. We welcome your attention, and we’ll see you next time

Recommended previous articles:

  • Mechanics-go Asynchronous task queues
  • Leaf-segment Distributed ID Generation System (Golang implementation version)
  • 10 GIFs to help you understand sorting algorithms (with go implementation code)
  • Go Book Recommendations (From Getting Started to Giving up)
  • Go parameter transfer type
  • Teach my sister how to write message queues
  • Cache avalanche, cache penetration, cache breakdown
  • Context package, read this article enough!!
  • Go -ElasticSearch: How to get started
  • Interviewer: Have you used for-range in go? Can you explain the reasons for these problems
  • Learn wire dependency injection, Cron timing task is actually so easy!
  • I heard you don’t know how to JWT or swagger. – I’m skipping meals and I’m here with my practice program
  • Master these Go features and you will improve your level by N levels