The premise

Before we introduce reflection in GO, we need to introduce interface. There are two types of interfaces in GO, eface and iface.

eface

All types of data in Go can be converted to eface, which corresponds to emptyInterface in Reflect

type eface struct {
	_type *_type // Type information
	data  unsafe.Pointer // The value to point to
}

type _type struct {
	size       uintptr  // The size of the content occupied by the type
	ptrdata    uintptr  // The size of the memory prefix containing all Pointers
	hash       uint32   / / type hash
	tflag      tflag    // Flag bit, mainly used for reflection
	align      uint8    // Align fields
	fieldAlign uint8   // The number of aligned bytes for the current structure field
	kind       uint8   // Base type enumeration value
	equal func(unsafe.Pointer, unsafe.Pointer) bool// Compare the types of the objects corresponding to the two parameters
	gcdata    *byte // Data of GC type
	str       nameOff // The offset of the type name string in the binary file segment
	ptrToThis typeOff // The offset of the type meta-information pointer in the binary file segment
}
Copy the code

Rutime. Runtime2. Go file

Eface consists mainly of Pointers to type information and values, which is easy to understand. The memory to which the data pointer points contains information about the type and value, that is, the data pointer points to eface itself

iface

Iface is mainly used to represent data that implements interface, corresponding to nonEmptyInterface in Reflect

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type itab struct {
	inter *interfacetype // Static type of interface. An abstract representation of an interface is a static interface, not an actual struct
	_type *_type // The actual type
	hash  uint32 // copy of _type.hash. Used for type switches. Like hash in _type, used for type assertion
	_     [4]byte
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. The functions implemented by the interface are consistent with the type of the interface
}
type interfacetype struct {
	typ     _type // Interface type
	pkgpath name / / package information
	mhdr    []imethod // The method abstraction of the interface is not the actual implemented method
}
Copy the code

The runtime. Runtime2. Go file

The iFace type field contains dynamic and static information for the type, as well as package and some function information.

Conversion of eFace and iFace

Unsafe. Pointer The _type field in eface and the inter field in iface can be converted via unsafe.Pointer if eface is of type Interface.

First, both are Pointers, so the alternative is unbroadening.Pointer.

Secondly, if we look carefully, we can find that the first field type of inter is _type.

Given the phenomenon, we venture a simple enough logic here: the in-memory model of type information in GO has fixed scope and rules.

For example, the first field type must be _type, and different types can have different fields depending on the type in addition to the _type.

So if an Interface type is turned to eface, then eface._type (*_type) can be turned to * interfaceType via unbroadening. Because the memory address to which the pointer *_type points contains interfaceType data.

Why can the pointer * interfaceType be converted to *_type? Since the first field of interfaceType is the _type, go can take the size of the _type from where the pointer starts.

reflect.Type

Let’s first look at the reflect.typeof code

func TypeOf(i interface{}) Type {
	/ / to eface
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}
Copy the code

Eface and reflect.emptyInterface are the same, so unsafe.Pointer converts emptyInterface directly, And then take the TYP property. The operations related to reflect.Type are based on *rtype

There are 26 basic types in GO, which are also enumerated in reflection

type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Ptr
	Slice
	String
	Struct
	UnsafePointer
)
Copy the code

In reflects. Type, many types have specific types, but only Interface data has special logic.

Here are the different structures that correspond to the different types

type arrayType struct {
	rtype
	elem  *rtype // array element type
	slice *rtype // slice type
	len   uintptr
}

type chanType struct {
	rtype
	elem *rtype  // channel element type
	dir  uintptr // Channel direction (ChanDir) Indicates the direction of chan
}

type funcType struct {
	rtype
	inCount  uint16  // Enter parameters
	outCount uint16 // top bit is set if last input parameter is ... Output parameters
}

type structType struct {
	rtype
	pkgPath name
	fields  []structField // sorted by offset
}

type ptrType struct {
	rtype // The type of pointer
	elem *rtype // pointer element (pointed at) type The type of the element to which the pointer points (static type)
}

type sliceType struct {
	rtype
	elem *rtype // slice element type
}

type mapType struct {
	rtype
	key    *rtype // map key type
	elem   *rtype // map element (value) type
	bucket *rtype // internal bucket structure
	// function for hashing keys (ptr to key, seed) -> hash
	hasher     func(unsafe.Pointer, uintptr) uintptr
	keysize    uint8  // size of key slot
	valuesize  uint8  // size of value slot
	bucketsize uint16 // size of bucket
	flags      uint32
}

type interfaceType struct {
	rtype
	pkgPath name      // import path
	methods []imethod // sorted by hash
}
Copy the code

All of the above types can pass*rtypeconversion

We’ll take a look at some of the reflects. Type functions to see how reflection takes the value of the _type and how each Type differs.

Method

The getfunction needs to distinguish whether it is of type Interface. If it is of type Interface, convert * rType to *interfaceType. Because the interfaceType type contains the function information field, so directly according to the index is ok, this logic is easy case.

func (t *interfaceType) Method(i int) (m Method) {
	if i < 0 || i >= len(t.methods) {
		return
	}
	p := &t.methods[i]
	/ / construction method
	pname := t.nameOff(p.name)
	m.Name = pname.name()
	if! pname.isExported() { m.PkgPath = pname.pkgPath()if m.PkgPath == "" {
			m.PkgPath = t.pkgPath.name()
		}
	}
	m.Type = toType(t.typeOff(p.typ))
	m.Index = i
	return
}
Copy the code

Interface type method code

If it is any other type, the function information needs to be exported. Since the function information is not displayed as a field, it needs to form a temporary structure to fetch it from memory

func (t *rtype) uncommon(a) *uncommonType {
	if t.tflag&tflagUncommon == 0 {
		return nil
	}
	switch t.Kind() {
	case Struct:
		return &(*structTypeUncommon)(unsafe.Pointer(t)).u
	case Ptr:
		type u struct {
			ptrType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Func:
		type u struct {
			funcType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Slice:
		type u struct {
			sliceType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Array:
		type u struct {
			arrayType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Chan:
		type u struct {
			chanType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Map:
		type u struct {
			mapType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Interface:
		type u struct {
			interfaceType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	default:
		type u struct {
			rtype
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	}
}
Copy the code

Uncommon () function

The uncommonType above is the structure that stores the function information

// Store the type of the function
type uncommonType struct {
	pkgPath nameOff // import path; empty for built-in types like int, string
	mcount  uint16  // number of methods
	xcount  uint16  // Number of exported methods
	moff    uint32  // offset from this uncommonType to [McOunt]method
	_       uint32  // unused
}

type method struct {
	name nameOff // name of method
	mtyp typeOff // method type (without receiver) Function type
	ifn  textOff // fn used in interface call (one-word receiver)
	tfn  textOff // fn used for normal method call
}
Copy the code

UncommonType type

With uncommonType, you can get the type information of the function

func (t *rtype) Method(i int) (m Method) {
	// If it is interface
	if t.Kind() == Interface {
		tt := (*interfaceType)(unsafe.Pointer(t))
		return tt.Method(i)
	}
	// Get the list of exported functions
	methods := t.exportedMethods()
	if i < 0 || i >= len(methods) {
		panic("reflect: Method index out of range")
	}
	p := methods[i]
	// Take the function name
	pname := t.nameOff(p.name)
	m.Name = pname.name()
	fl := flag(Func)
	mtyp := t.typeOff(p.mtyp)
	ft := (*funcType)(unsafe.Pointer(mtyp))
	in := make([]Type, 0.1+len(ft.in()))
	// Add the current type to in
	in = append(in, t)
	// Add the parameter type to in
	for _, arg := range ft.in() {
		in = append(in, arg)
	}
	// Returns an array of value types
	out := make([]Type, 0.len(ft.out()))
	for _, ret := range ft.out() {
		out = append(out, ret)
	}

	// Build the function
	mt := FuncOf(in, out, ft.IsVariadic())
	m.Type = mt
	tfn := t.textOff(p.tfn)
	fn := unsafe.Pointer(&tfn)
	m.Func = Value{mt.(*rtype), fn, fl}

	m.Index = i
	return m
}
Copy the code

* Rtype Method Method

FuncOf takes the receiver as the first input parameter, so it needs to re-form * rType as the Type of method. So if you want to Call the Call Method using method.func, you need to pass in the receiver as the first argument.

The FuncOf method is longer, so I won’t post the code here. Briefly explain the main logic

  • Create a newfuncType
  • Assign input and output parameters, andhashValue (mixed with variable function judgment)
  • Find funcTypes of the same type in the cache and return them if they exist
  • I’m going to look it up in the linker, whatever it is, put it in the cache and return it

Field

The type to get the Field must be Struct

func (t *rtype) Field(i int) StructField {
	ift.Kind() ! = Struct {panic("reflect: Field of non-struct type " + t.String())
	}
	/ / to structType
	tt := (*structType)(unsafe.Pointer(t))
	return tt.Field(i)
}
Copy the code

* Rtype Field method

As you can see, the type is determined and then converted to *structType

type structType struct {
	rtype
	pkgPath name
	fields  []structField // sorted by offset
}
Copy the code

StructType structure

The structType method for getting a Field is very simple

func (t *structType) Field(i int) (f StructField) {
	if i < 0 || i >= len(t.fields) {
		panic("reflect: Field index out of bounds")
	}
	p := &t.fields[i]
	f.Type = toType(p.typ)
	f.Name = p.name.name()
	f.Anonymous = p.embedded()
	if! p.name.isExported() { f.PkgPath = t.pkgPath.name() }iftag := p.name.tag(); tag ! ="" {
		f.Tag = StructTag(tag)
	}
	f.Offset = p.offset()
	f.Index = []int{i}
	return
}
Copy the code

Implements

func (t *rtype) Implements(u Type) bool {
	if u == nil {
		panic("reflect: nil type passed to Type.Implements")}ifu.Kind() ! = Interface {panic("reflect: non-interface type passed to Type.Implements")}return implements(u.(*rtype), t)
}
Copy the code

*rtype Implements method

This method means: whether t implements u

In go, we get u as Interface: reflect.typeof ((* fmt.stringer)(nil)).elem ()

Declare an empty interface pointer and take its Elem. Why is this method available, you need to look at the Elem method

func (t *rtype) Elem(a) Type {
	switch t.Kind() {
	case Array:
		tt := (*arrayType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Chan:
		tt := (*chanType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Map:
		tt := (*mapType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Ptr:
		tt := (*ptrType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Slice:
		tt := (*sliceType)(unsafe.Pointer(t))
		return toType(tt.elem)
	}
	panic("reflect: Elem of invalid type " + t.String())
}
Copy the code

* Elem method of rtype

From the code above, we know that t’s Kind is now a pointer, so we convert it to ptrType, and then take the ptrType.elem field as the type. So the ELEm field should point to a static type. The above elem for Array, Chan, and so on are static types.

Then the logic of implements. Implements (); implements (); implements (); implements (); implements (); implements ()

  • Let’s say t is of type Interface

    if V.Kind() == Interface {
    		v := (*interfaceType)(unsafe.Pointer(V))
    		i := 0
    		for j := 0; j < len(v.methods); j++ {
    			tm := &t.methods[i]
    			tmName := t.nameOff(tm.name)
    			vm := &v.methods[j]
    			vmName := V.nameOff(vm.name)
    			// The function name is the same as the type
    			// an rtype must be used for functions with the same input and output parameters
    			if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
    				if! tmName.isExported() { tmPkgPath := tmName.pkgPath()if tmPkgPath == "" {
    						tmPkgPath = t.pkgPath.name()
    					}
    					vmPkgPath := vmName.pkgPath()
    					if vmPkgPath == "" {
    						vmPkgPath = v.pkgPath.name()
    					}
    					iftmPkgPath ! = vmPkgPath {continue}}if i++; i >= len(t.methods) {
    					return true}}}return false
    	}
    Copy the code
  • Suppose it’s not Interface

    // Instead of interface, determine the actual function
    	v := V.uncommon()
    	if v == nil {
    		return false
    	}
    	i := 0
    	vmethods := v.methods()
    	for j := 0; j < int(v.mcount); j++ {
    		tm := &t.methods[i]
    		tmName := t.nameOff(tm.name)
    		vm := vmethods[j]
    		vmName := V.nameOff(vm.name)
    		if vmName.name() == tmName.name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) {
    			if! tmName.isExported() { tmPkgPath := tmName.pkgPath()if tmPkgPath == "" {
    					tmPkgPath = t.pkgPath.name()
    				}
    				vmPkgPath := vmName.pkgPath()
    				if vmPkgPath == "" {
    					vmPkgPath = V.nameOff(v.pkgPath).name()
    				}
    				iftmPkgPath ! = vmPkgPath {continue}}if i++; i >= len(t.methods) {
    				return true}}}Copy the code

We can see that the above two pieces of code have the same logic and simple, both are to determine whether the type and name are the same, if the non-exported functions need to determine whether the package name is the same

AssignableTo

func (t *rtype) AssignableTo(u Type) bool {
	if u == nil {
		panic("reflect: nil type passed to Type.AssignableTo")
	}
	uu := u.(*rtype)
	// Determine the type
	return directlyAssignable(uu, t) || implements(uu, t)
}
Copy the code

* AssignableTo method of rtype

Whether t can be assigned to u

DirectlyAssignable () : directlyAssignable (

func directlyAssignable(T, V *rtype) bool {
	// x's type V is identical to T?
	if T == V {
		return true
	}

	// Otherwise at least one of T and V must not be defined
	// and they must have the same kind.
	ifT.hasName() && V.hasName() || T.Kind() ! = V.Kind() {return false
	}

	if T.Kind() == Chan && specialChannelAssignability(T, V) {
		return true
	}

	// x's type T and V must have identical underlying types.
	return haveIdenticalUnderlyingType(T, V, true)}Copy the code

The directlyAssignable method in reflect.type.go

To explain the above logic:

  • *rtypeEqual, assignable
  • KindMust be equal, and the name of one of the types must be null
  • A type ofchan“, need to judgeElemchanThe direction of the

In terms of type names, some basic types have names and some don’t

  • Bool – Complex128 ranges from 1 to 16 and has a name
  • 17 no name array
  • Chan, 50-18 without a name
  • Func is 51-19. No name
  • Interface 20 have a name
  • The map 53-21 without a name
  • PTR 54-22 without a name
  • Slice 23 no name
  • A string of 24 has a name
  • Struct 25 anonymous no name, other
  • The unsafe. 58-26 has a Pointer

Struct anonymous data does not have a name. Struct anonymous data does not have a name

Then see haveIdenticalUnderlyingType method

if Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer {
		return true
	}

	// Composite types.
	switch kind {
	case Array:
		return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Chan:
		return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Func:
		t := (*funcType)(unsafe.Pointer(T))
		v := (*funcType)(unsafe.Pointer(V))
		ift.outCount ! = v.outCount || t.inCount ! = v.inCount {return false
		}
		for i := 0; i < t.NumIn(); i++ {
			if! haveIdenticalType(t.In(i), v.In(i), cmpTags) {return false}}for i := 0; i < t.NumOut(); i++ {
			if! haveIdenticalType(t.Out(i), v.Out(i), cmpTags) {return false}}return true

	case Interface:
		t := (*interfaceType)(unsafe.Pointer(T))
		v := (*interfaceType)(unsafe.Pointer(V))
		if len(t.methods) == 0 && len(v.methods) == 0 {
			return true
		}
		return false

	case Map:
		return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Ptr, Slice:
		return haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Struct:
		t := (*structType)(unsafe.Pointer(T))
		v := (*structType)(unsafe.Pointer(V))
		if len(t.fields) ! =len(v.fields) {
			return false
		}
		ift.pkgPath.name() ! = v.pkgPath.name() {return false
		}
		for i := range t.fields {
			tf := &t.fields[i]
			vf := &v.fields[i]
			iftf.name.name() ! = vf.name.name() {return false
			}
			if! haveIdenticalType(tf.typ, vf.typ, cmpTags) {return false
			}
			ifcmpTags && tf.name.tag() ! = vf.name.tag() {return false
			}
			iftf.offsetEmbed ! = vf.offsetEmbed {return false}}return true
	}
Copy the code

Simple types can be as long as kind is consistent, and complex types have their own judgments, but the logic is very simple

reflect.Value

Reflect. Value can be obtained from reflect.ValueOf. Reflect. ValueOf mainly calls the unpackEface method to unbox a Value

func unpackEface(i interface{}) Value {
	/ / to eface
	e := (*emptyInterface)(unsafe.Pointer(&i))
	// NOTE: don't read e.word until we know whether it is really a pointer or not.
	t := e.typ
	if t == nil {
		return Value{}
	}
	f := flag(t.Kind())
	if ifaceIndir(t) {
		f |= flagIndir
	}
	return Value{t, e.word, f}
}

func ifaceIndir(t *rtype) bool {
	return t.kind&kindDirectIface == 0
}

kindDirectIface = 1 << 5 / / 32
Copy the code

UnpackEface function

Similar to Reflect. TypeOf, which is converted to emptyInterface first. If there is a logic ifaceIndir | = flagIndir} {f (t), if the original kind in 32 t equals 0, then is indirect. Most types are indirect, with only the following types being direct:

  • map
  • chan
  • func
  • ptr
  • unsafe.Pointer

For direct or indirect types, there is a different processing logic when boxed into interfaces (that is, when the Interface() method is called).

As with reflect.Type, we analyze reflect.Value in a few key ways, and take a look at the implementation of reflect.Value

Method

Get the Value of method

func (v Value) Method(i int) Value {
	if v.typ == nil {
		panic(&ValueError{"reflect.Value.Method", Invalid})
	}
	// is itself a method
	ifv.flag&flagMethod ! =0 || uint(i) >= uint(v.typ.NumMethod()) {
		panic("reflect: Method index out of range")}if v.typ.Kind() == Interface && v.IsNil() {
		panic("reflect: Method on nil interface value")}// Add the function-dependent flag complement
	fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
	fl |= flag(Func)
	fl |= flag(i)<<flagMethodShift | flagMethod
	// Type is not the type of Method, but the type of Method receiver
	return Value{v.typ, v.ptr, fl}
}
Copy the code

Value Method Method of the Value

The code for the Method Method is simple, just add flag bits to it. Neither type nor PTR has changed, and the meaning of this will be seen in the Call method.

Call

func (v Value) Call(in []Value) []Value {
	v.mustBe(Func)
	v.mustBeExported()
	return v.call("Call", in)
}
Copy the code

The Call method of Value

We see that the Call implementation relies primarily on the Call method. The initial logic of the Call method looks like this:

func (v Value) call(op string, in []Value) []Value {
// Whatever v.type is, convert it to funcType first
	t := (*funcType)(unsafe.Pointer(v.typ))
	var (
		fn       unsafe.Pointer
		rcvr     Value
		rcvrtype *rtype
	)
	// v is actually the receiver
	ifv.flag&flagMethod ! =0 {
		rcvr = v
		rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
	} else ifv.flag&flagIndir ! =0 {
		fn = *(*unsafe.Pointer)(v.ptr)
	} else {
		fn = v.ptr
	}
Copy the code

If the Value is obtained using the Method Method, the methodReceiver Method needs to handle it.

func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *funcType, fn unsafe.Pointer) {
	i := methodIndex
	if v.typ.Kind() == Interface {
		// Change to the interface type first
		tt := (*interfaceType)(unsafe.Pointer(v.typ))
		if uint(i) >= uint(len(tt.methods)) {
			panic("reflect: internal error: invalid method index")}// retrieve method from the interface
		m := &tt.methods[i]
		if! tt.nameOff(m.name).isExported() {panic("reflect: " + op + " of unexported method")}/ / to iface
		iface := (*nonEmptyInterface)(v.ptr)
		if iface.itab == nil {
			panic("reflect: " + op + " of method on nil interface value")}// Dynamic type type
		rcvrtype = iface.itab.typ
		// Fn pointer to the dynamic type
		fn = unsafe.Pointer(&iface.itab.fun[i])
		FuncType statically, because it is the same as funcType dynamically
		t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ)))
	} else {
		rcvrtype = v.typ
		// A collection of methods that can be exported
		ms := v.typ.exportedMethods()
		if uint(i) >= uint(len(ms)) {
			panic("reflect: internal error: invalid method index")
		}
		m := ms[i]
		if! v.typ.nameOff(m.name).isExported() {panic("reflect: " + op + " of unexported method")
		}
		ifn := v.typ.textOff(m.ifn)
		fn = unsafe.Pointer(&ifn)
		t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp)))
	}
	return
}
Copy the code

MethodReceiver method

The methodReceiver gets the corresponding method type, pointer, and receiver type.

The call method then does some variable parameter judgment and combines the parameter values passed in, and then calls Runtime. call.

call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))

The above method actually calls the function. Then combine the return Value into []Value. The combination logic of return Value mainly involves the displacement operation of pointer address, which will not be described here.

Convert

func (v Value) Convert(t Type) Value {
	ifv.flag&flagMethod ! =0 {
		v = makeMethodValue("Convert", v)
	}
	op := convertOp(t.common(), v.typ)
	if op == nil {
		panic("reflect.Value.Convert: value of type " + v.typ.String() + " cannot be converted to type " + t.String())
	}
	return op(v, t)
}
Copy the code

Value Convert function

Indicates that v is converted to a Value of type t. Reflect.typeof ().convertibleto is also implemented by calling convertOp. This method is also called for reflection assignments including fields under Model in Brom. This method has problems converting numbers to strings.

func convertOp(dst, src *rtype) func(Value, Type) Value {
	switch src.Kind() {
	case Int, Int8, Int16, Int32, Int64:
		switch dst.Kind() {
		case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
			return cvtInt
		case Float32, Float64:
			return cvtIntFloat
		case String:
			return cvtIntString
		}

	case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
		switch dst.Kind() {
		case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
			return cvtUint
		case Float32, Float64:
			return cvtUintFloat
		case String:
			return cvtUintString
		}

	case Float32, Float64:
		switch dst.Kind() {
		case Int, Int8, Int16, Int32, Int64:
			return cvtFloatInt
		case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
			return cvtFloatUint
		case Float32, Float64:
			return cvtFloat
		}

	case Complex64, Complex128:
		switch dst.Kind() {
		case Complex64, Complex128:
			return cvtComplex
		}

	case String:
		if dst.Kind() == Slice && dst.Elem().PkgPath() == "" {
			switch dst.Elem().Kind() {
			case Uint8:
				return cvtStringBytes
			case Int32:
				return cvtStringRunes
			}
		}

	case Slice:
		if dst.Kind() == String && src.Elem().PkgPath() == "" {
			switch src.Elem().Kind() {
			case Uint8:
				return cvtBytesString
			case Int32:
				return cvtRunesString
			}
		}

	case Chan:
		if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
			return cvtDirect
		}
	}

	// dst and src have same underlying type.
	if haveIdenticalUnderlyingType(dst, src, false) {
		return cvtDirect
	}

	// dst and src are non-defined pointer types with same underlying base type.
	if dst.Kind() == Ptr && dst.Name() == "" &&
		src.Kind() == Ptr && src.Name() == "" &&
		haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) {
		return cvtDirect
	}

	if implements(dst, src) {
		if src.Kind() == Interface {
			return cvtI2I
		}
		return cvtT2I
	}

	return nil
}
Copy the code

So let’s look at the conversion of numbers to strings, so let’s look at cvtIntString

func cvtIntString(v Value, t Type) Value {
	return makeString(v.flag.ro(), string(v.Int()), t)
}

func makeString(f flag, v string, t Type) Value {
	ret := New(t).Elem()
	ret.SetString(v)
	ret.flag = ret.flag&^flagAddr | f
	return ret
}
Copy the code

In go, you cannot convert an int toa string. We usually call strconv.itoa () to convert a decimal number. If you’re interested, take a look at strconv.itoa (), which is the equivalent of breaking numbers into strings, bit by bit

All other conversions should be normal, checking whether the type can be converted and then returning a different conversion function

Elem

Next, take a look at the Elem method, which focuses on PTR and interface

func (v Value) Elem(a) Value {
	k := v.kind()
	switch k {
	case Interface:
		var eface interface{}
		if v.typ.NumMethod() == 0 {
			eface = *(*interface{})(v.ptr)
		} else {
			eface = (interface({}) (* *interface {
				M() // Fear of losing some type information when cast to eface
			})(v.ptr))
		}
		x := unpackEface(eface)
		ifx.flag ! =0 {
			x.flag |= v.flag.ro()
		}
		// interface Specifies the actual type
		return x
	case Ptr:
		ptr := v.ptr
		// PTR needs to refetch the pointer to the pointer
		ifv.flag&flagIndir ! =0 {
			ptr = *(*unsafe.Pointer)(ptr)
		}
		// The returned value's address is v's value.
		if ptr == nil {
			return Value{}
		}
		tt := (*ptrType)(unsafe.Pointer(v.typ))
		typ := tt.elem
		/ / change the flag
		fl := v.flag&flagRO | flagIndir | flagAddr
		fl |= flag(typ.Kind())
		return Value{typ, ptr, fl}
	}
	panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
Copy the code

Let’s say logic of type PTR:

  • First toptrType
  • takeptrType.elemElement type as newValueWe know thatelemRefers to the static type of the element
  • thenflagBit or some flag bit

There is an if judgment, if v.lag&flagindir! = 0 {PTR = *(*unsafe.Pointer)(PTR)} we didn’t say that. When does it get into this if logic?

Now, if the data is a pointer to a pointer, and we call Elem, flag takes the flagIndir bit, we see that only TYP and flag change, PTR does not change, so PTR is still a pointer to a pointer. If we now call the Elem Value again, it will go into the if block above.

What’s going on in the if block? Convert PTR to a pointer to a pointer, and then evaluate, and you get a pointer. That is, PTR is now a normal pointer to a value. Remember this logic, which we’ll see when Value is boxed as interface.

Logic of type Interface:

  • To emptyinterfaceIf you have a function, you need to convert it to a bandM()Methods the emptyinterfaceFear of losing type information
  • The emptyinterfaceOut of the box intoValueHere,ValueIt becomes dynamic

Interface

Interface returns the actual Value of Value

func (v Value) Interface(a) (i interface{}) {
	return valueInterface(v, true)}func valueInterface(v Value, safe bool) interface{} {
	if v.flag == 0 {
		panic(&ValueError{"reflect.Value.Interface", Invalid})
	}
	ifsafe && v.flag&flagRO ! =0 {
		panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")}ifv.flag&flagMethod ! =0 {
		v = makeMethodValue("Interface", v)
	}

	if v.kind() == Interface {
		// Special case: return the element inside the interface.
		// Empty interface has one layout, all interfaces with
		// methods have a second layout.
		if v.NumMethod() == 0 {
			return* (*interface{})(v.ptr)
		}
		return* (*interface {
			M()
		})(v.ptr)
	}
	return packEface(v)
}
Copy the code

The main thing is to call the packEface method and box the Value into Interface

func packEface(v Value) interface{} {
	t := v.typ
	var i interface{}
	e := (*emptyInterface)(unsafe.Pointer(&i))
	switch {
	// Indirect memory
	case ifaceIndir(t):
		if v.flag&flagIndir == 0 {
			panic("bad indir")
		}
		ptr := v.ptr
		// the description is addressable
		ifv.flag&flagAddr ! =0 {
			c := unsafe_New(t)
			typedmemmove(t, c, ptr)
			// Make a new address assignment
			ptr = c
		}
		// it is not addressable, indicating that it is a copy of the value
		e.word = ptr
	casev.flag&flagIndir ! =0: 
		e.word = *(*unsafe.Pointer)(v.ptr)
	default:
		// Value is direct, and so is the interface. It's a pointer
		e.word = v.ptr
	}
	e.typ = t
	return i
}
Copy the code

If the address is indirect:

  • Addressable,newA new pointerptrIs copied to the new pointer and assigned toe.word. Change returnedinterfaceIt doesn’t affect the original value
  • Unaddressable, indicating a value copy, directly assignede.word

Direct address with the indirect flag in the flag bit:

  • A pointer to a pointer, then evaluates to a pointer and assigns toe.word

There is a problem with the type of direct address, such as map. We declare A map pointer to A and then do B:= reflect.valueof (A).elem ().elem (). And B’s PTR is now a pointer to map, because the second Elem has converted the pointer to a pointer. Then we call b.interface (), at which point we find:

  • Bmap, is the direct type
  • BThe flag bit of theflagIndirThe indirect sign
  • performe.word = *(*unsafe.Pointer)(v.ptr)operation

Even though unsafe.Pointer can be converted to any Pointer, the address of the Pointer will definitely change.

But! But! This code works!! So this piece of code eventually points to the correctmap

So let’s guess, for direct types, Pointers are Pointers that can be converted to Pointers. But the following single test panic, can not prove this logic:

	a := map[string]string{"cc": "dd"}
	c := unsafe.Pointer(&a)
	fmt.Println(c)

	d := *(*unsafe.Pointer)(c)
	fmt.Println(d)

	f := *(*map[string]string)(d)
	fmt.Println(f) 
Copy the code

It’s a piece of logic I can’t figure out!