The implementation of the Go reflection is closely related to interface and unbroadening.Pointer. If you’re not already familiar with Golang’s underlying implementation of interface, check out my previous article: The Interface underlying implementation of the Go language, unsafe.Pointer will be covered in a future article. (The current Go environment used in this article is Go 1.12.9)

Interface to review

First of all, let’s briefly review the structure of interface. In general, it is:

It is subdivided into iface with functions and eface without functions (interface{}).

There is no functioneface

The function ofiface

The static type(Static interface type) andDynamic mixed type(Dynamic Concrete Type)

In Go, each variable has a unique static type that can be determined at compile time. Some variables may have dynamic mixed types in addition to static types.

For example:

// Interface with function
var r io.Reader 


tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
iferr ! =nil {
    return nil, err
}


r = tty


// Interface with no function
var empty interface{}
empty = tty
Copy the code

The function ofifaceAn example of

Var r IO.Reader

Lines 4 through 7 simply assign and get an instance of * os.file, but forget about it. R = tty

There is no functionefaceAn example of

Var empty interface{}

And finally empty equals tty

But remember: although there are dynamic mixed types, the external “representation” is still static.

Introduction to Go Reflection

There are three laws of Go reflection:

Reflection goes from interface value to Reflection object. // Reflection object ===> Interface data. 2 From Reflection Object to interface Value. 3. To modify a reflection object, the value must be settable.Copy the code

The reflection of Go is the implementation of these three laws.

The reflection of Go consists of two parts: Type and Value. Type and Value are two structures.

Type:

type Type interface {
    Align() int
    FieldAlign() int
    Method(int) Method
    MethodByName(string) (Method, bool)
    NumMethod() int
    Name() string
    PkgPath() string
    Size() uintptr
    String() string
    Kind() Kind
    Implements(u Type) bool
    AssignableTo(u Type) bool
    ConvertibleTo(u Type) bool
    Comparable() bool
    Bits() int
    ChanDir() ChanDir
    IsVariadic() bool
    Elem() Type
    Field(i int) StructField
    FieldByIndex(index []int) StructField
    FieldByName(name string) (StructField, bool)
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    In(i int) Type
    Key(a) Type
    Len(a) int
    NumField(a) int
    NumIn(a) int
    NumOut(a) int
    Out(i int) Type
    common(a) *rtype
    uncommon(a) *uncommonType
}
Copy the code

Value:

type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}
Copy the code

You will notice that the reflection implementation is similar to the composition of an interface, consisting of “type” and “data value”, but it is worth noting that the “type” and “data value” of the interface are “together”, while the “type” and “data value” of the reflection are separate.

Type and Value provide a variety of methods: getting the list of attributes of an object, getting and modifying the Value of an attribute, the name of the structure to which the object belongs, the underlying Type of the object, and so on

Reflection in Go has two core functions in use:

  • reflect.TypeOf(x)
  • reflect.ValueOf(x)

These two functions convert a given data object to Type and Value, respectively. Both of these are called reflection objects

Reflection goes from interface value to Reflection Object

Given a data object, the data object can be converted into reflection objects Type and Value.

Example code:

package main

import (
    "fmt"
    "reflect"
)

func main(a) {
    var x float64 = 3.4

    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)

    fmt.Println("type:", t)   //type: float64

    fmt.Println("value:", v.String())  //value: <float64 Value>
    fmt.Println("type:", v.Type()) // type: float64
    fmt.Println("kind is float64:", v.Kind() == reflect.Float64) //kind is float64: true
    fmt.Println("value:", v.Float()) / / value: 3.4

}

Copy the code

As you can see from line 17: Value also gets the Type of the current data Value. Therefore, the graph of rule 1 should be:

Reflection goes from Reflection object to interface value.

A given reflection object can be converted to some type of data object. It’s the reverse of rule one.

Notice that you can’t reverse Type, and it makes sense to think about it, what if the invertible Type was converted to? (# ^. ^ #)

Code for rule 1:

package main

import (
    "fmt"
    "reflect"
)

func main(a) {
    var x float64 = 3.4

    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)

    ...

    o := v.Interface().(float64) // Code for rule 2
    fmt.Println(o)

}
Copy the code

To modify a reflection object, the value must be settable.

Rule three says: by reflecting objects, you can modify the content of the original data.

The reflected object here refers to Value. After all, Type only represents the content related to the Type of the original data, while Value corresponds to the original data object itself.

In all of the examples so far, reflected changes to the Value object do not directly modify the original data object.

package main

import (
    "fmt"
    "reflect"
)

func main(a) {
    var x float64 = 3.4

    t := reflect.TypeOf(x)
    v := reflect.ValueOf(&x)

    ....

    o := v.Interface().(float64)
    fmt.Println(o)

    v.SetFloat(5.4) // This line will report an error
    fmt.Println(x)

}
Copy the code

This code will declare a panic at 20 lines

reflect: reflect.Value.SetFloat using unaddressable value
Copy the code

The object v is settable. The address is not settable.

We can use the CanSet() method of the Value structure to see if the new Value can be set or modified. The following code tells you that CanSet() returns false.

fmt.Println(v.CanSet()) // false
Copy the code

How do I modify the value of the original data object by reflecting it?

How can I change the value of the original data object by reflecting it or why not?

The reason is simple and pure: In Go, arguments to any function are copies of values, not original data.

The reflect.valueof () function is no exception. The reflection objects we get are the copy of the original object, rather than the original object itself, so we can not modify to the original object; Even if you can modify, modify a reference when the copy, also meaningless, better to report an error. This leads to the settable property of Go reflection from the third law, and extends to methods like CanSet().

So how do you fix it?

First, in order for a function to have “side effects” in Go, a value must be passed as a pointer type.

.var x float64 = 3.4
    v := reflect.ValueOf(&x)

    ...
Copy the code

The value type (*v) of the current type must be obtained. Go provides another method, Elem()

.var x float64 = 3.4
    v := reflect.ValueOf(&x)

    p := v.Elem()
    fmt.Println(p.CanSet()) // true
    
    p.SetFloat(7.1)
    fmt.Println(x) / / 7.1
Copy the code

Look at the above code, you can modify the original data.

Reflection principle

It’s not hard to see how similar go’s reflection and interface are in structure! Both are divided into two parts: one is Type and the other is value.

Will reflection be implemented with respect to interface?

What does reflection mean? Reflection means being able to dynamically know the type and structure of a given data object at run time and have the opportunity to modify it! Now a data object, how do you determine what structure it is? Data interface contains structural data ah, as long as you try to get the corresponding memory address of the data, and then convert the data into interface, by checking the type structure of the interface, you can know the structure of the data ah ~ in fact, the above is Go reflection popular principle.

The figure can be shown as:

Unsafe.Pointer the unsafe.Pointer is a Pointer that can point to any data in the Go system.

The source code section (the following section can be ignored, which is a little bit of a pit WHEN I look at the code).

Let’s look at the specific source code: source code in the “GO SDK/ SRC /refelct” package, specific mainly package “type. GO “and “value. GO” these two files.

It is easy to think of reflection’s core code as the reflect.valueof () and reflect.typeof () functions.

TypeOf() : reflect.typeof ()

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}
Copy the code

There are two data structures, one is Type and one is emptyInterface. Look at the code for both: emptyInterface is in the “GO SDK/ SRC /reflect/value.go” file

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
    typ  *rtype
    word unsafe.Pointer
}

// nonEmptyInterface is the header for an interface value with methods.
type nonEmptyInterface struct {
    // see .. /runtime/iface.go:/Itab
    itab *struct {
        ityp *rtype // static interface type
        typ  *rtype // dynamic concrete type
        hash uint32 // copy of typ.hash
        _    [4]byte
        fun  [100000]unsafe.Pointer // method table
    }
    word unsafe.Pointer
}
Copy the code

If you look closely, there are two structures: the empty interface and the interface that contains the method. And consistent with eface and iface content fields! Isn’t there eface and iFace? What’s the difference between the two?

After checking the code, I found:

Interface source code (located in the “Go to the SDK/SRC/runtime/runtime2. Go”) will eface and iface and reflection of the source code (located in the “Go SDK/ SRC /reflect/value.go “) emptyInterface and nonEmptyInterface keep data in sync!

In addition, the interface source (located in “Go SDK/ SRC /runtime/type.go”) and the rtype source (located in “Go SDK/ SRC /reflect/type.go”) also keep data synchronized.

For more exciting content, please follow my wechat official accountInternet Technology NestOr add wechat to discuss and exchange:

References:

Go 1.12.9 reflex source: / SRC/reflect/package Go 1.12.9 interface source: / SRC/runtime/runtime2. Go and other studygolang.com/articles/21… Blog.golang.org/laws-of-ref… Blog.csdn.net/u011957758/… Draveness. Me/golang/docs… Mp.weixin.qq.com/s/Hke0mSCEa…