Go version 1.14.12

What is the Duck Type

When a bird is seen walking like a duck, swimming like a duck, and quacking like a duck, it can be called a duck.

  


Show me the code 🤔

type Duck interface {
	Quack()
}
type Cat struct{}
//The Cat implements the Quack() method, now, the Cat is Duck Type
func (c Cat) Quack(a) {
	fmt.Println("Miowo")}func main(a) {
	var _ Duck = Cat{}
}
Copy the code

“I don’t know how to Quack, but I know how to Quacks 😁.”

The compiler did some calculation and ruthlessly underlined ❌


Problem analysis

  1. Fault location: When it appears in the compile process, check the compile package.
  2. Error location: Search the key fields of error information globally in compile packagedoes not implement.
  3. Sifting through it, there it issubr.go

*why = FMT.Sprintf(“:\n\t%v does not implement %v (missing %v method)”, SRC, DST, missing.Sym)

Context discovery within a function (note the code comments) :

// 3. dst is an interface type and src implements dst.
	ifdst.IsInterface() && src.Etype ! = TNIL {var missing, have *types.Field
		var ptr int
        // determine whether SRC implements DST
		if implements(src, dst, &missing, &have, &ptr) {
			return OCONVIFACE
		}
        / /... omit
    }
Copy the code

But what is SRC and what is DST?

It seems that we still need to understand the program compilation process. The steps are as follows:

  1. Lexical and grammatical analysis
  2. Type checking
  3. Intermediate code generation
  4. Machine code generation

Obviously, our problem is in the type checking phase.

SRC/DST data structure *types.Type: nod is associated with gC.node. In the phase of type checking, the generation of abstract syntax tree has been completed.

Var Duck = Cat{} var Duck = Cat{} var Duck = Cat{} Makes it impossible to pass implements()

With the Debug tool in our IDE, we can determine the code flow as follows: gc.Main -> typecheckslice-> typecheck ->typecheck1 -> typecheckas ->assignconv-> assignconvfn ->assignop -> implements


The code logic is as follows:

Graph of TD A [building] -- > | lexical and syntax analyzer (abstract syntax tree) | B B -- -- > C (traversal type checking func body) C - > D (when the Op is assignment statements) D -- -- > E (typecheckas) E > | n.R d.light,  n.Left.Type|F(implements)

Take a look at the structure of n.light, n.left.type, well, it looks a bit complicated, but let’s just focus on the fields we need to focus on

n.Right => Cat Struct

n.Left.Type => Duck Type

Source code analysis

package gc

func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
	t0 := t
	if t == nil {
		return false
	}
	/ /... Part of the code is omitted
	t = methtype(t)
	var tms []*types.Field
	ift ! =nil {
		expandmeth(t)
		tms = t.AllMethods().Slice()
	}
	i := 0
	// Walk through the methods in Duck
	for _, im := range iface.Fields().Slice() {
		if im.Broke() {
			continue
		}
		// I is the number of methods in Cat and Duck
		for i < len(tms) && tms[i].Sym ! = im.Sym { i++ }// If the number of different methods in Cat and Duck is equal to the total number of methods in Cat, it is clear that Cat does not implement Duck
       // If the number of different elements in A and B is equal to the number of elements in A, then there must be no element in A that is identical to B. A does not implement B
		if i == len(tms) {
			*m = im
			*samename, _ = ifacelookdot(im.Sym, t, true)
			*ptr = 0
			return false
		}
		tm := tms[i]
		iftm.Nointerface() || ! types.Identical(tm.Type, im.Type) { *m = im *samename = tm *ptr =0
			return false
		}
		followptr := tm.Embedded == 2
		
		/ /... Part of the code is omitted
	}
	
	// We're going to emit an OCONVIFACE.
	// Call itabname so that (t, iface)
	// gets added to itabs early, which allows
	// us to de-virtualize calls through this
	// type/interface pair later. See peekitabs in reflect.go
	ifisdirectiface(t0) && ! iface.IsEmptyInterface() { itabname(t0, iface) }return true
}
Copy the code

Above, is the compiler to determine whether Cat is duck type of the general process, there are more details we are interested in can explore their own source code, I hope we explore the principle of the process of thinking can bring you some inspiration.

The Debug method

The Debug process is as follows:

1. View the file directory

2. Open the GolandsrcSource code, modify Debug run configuration, start Debug

3. Place the cursor where the code can be run and clickRun to the cursorTo view the function call flow

Goland Debug visualization, great convenience for us to view the details of the code running process, I hope you can make full use of.