Original address
For the first time I translated an article, please give me some advice from all walks of life!
Types and interfaces
Because mapping is based on types, first we will give a new introduction to types.
go is a static language, and each variable has a static type, so each variable has a clear variable type during the compilation stage, such as: int, float32, and MyType. . .
for example:
type MyInt int var i int var j MyInt
The type of variable i is int, the type of variable j is MyInt, and the variables i and j have a certain type. Although the potential types of i and j are the same, they cannot assign values to each other without conversion.
An important category among types is interface type, which is a collection of a series of methods. An interface variable can store any specific value declared in an interface method. Like and is a good example, these two interfaces are defined in the io package.
type Reader interface{ Read(p []byte)(n int, err error) } type Writer interface{ Writer(p []byte)(n int,er error) }
Any variable declared as or type can be used with the Read or Writer method. This means that variables of type can be assigned any variable with Read method.
var r r = r = (r) r = new()
No matter what type of value is assigned to the variable r, the type of variable r is still. Go is a statically typed language, and the type of r is always.
There is an important extreme interface type among interface types - empty interface.
interface{}
It represents an empty set of methods and can be assigned to any value, because any variable has 0 or more methods.
There is a wrong saying that the interface types of go are defined dynamically. In fact, they are defined statically in go. A variable of an interface type always has the same type. Although the values stored in the interface type variable have different types during operation, the variable of the interface type is always static.
How to represent interfaces
About the representation method of interface types in go, Russ Cox has introduced in detail in a blog [blog:/2009/12/]
A variable of an interface type stores a pair of information: specific values, and a description of the type of value. More specifically, the value is the underlying specific data item of the implementation interface, and the type is a complete description of the data item type.
For example:
var r tty, err := ("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } r = tty
The variable r contains two data items: value (tty), type (). Note that the implementation method is not just Read. Even if the interface type only contains Read method, the value (tty) is used for its complete type information, so we can call it as follows
var w w = r.()
This statement is an assertion statement, which means that the data item in the variable r is declared as, because we can assign r to w. After executing this statement, the variable w will contain the value (tty) and type (*) just like r. Even though the specific value may contain many methods, the static type of an interface determines which method can be called through interface-type variables.
We can
var empty interface{} empty = w
This interface variable also contains a data pair (tty,*). An empty interface can accept any type of variable and contains all the information we may use about this variable. We don't need to assert here because the w variable is satisfied with the empty interface. In the previous example of moving data from Reader to Writer, we need type assertions because the Reader interface does not include Writer methods
Remember that the contents of the interface data pair can only come from (value, concrete type) rather than (value, interface type), that is, the interface type cannot accept variables of the interface type.
1. From interface type to map object
At the bottom, mapping is a mechanism for interpreting the pairs (values, types) of data stored inside the interface. First of all, we need to know the two types Type and Value in the reflect package. These two types provide access to the contents of the interface variables, and retrieve variables of the interface type at the same time with two methods.
First we start TypeOf
package main import ( "fmt" "reflect" ) func main() { var f float64 = 13.4 ((f)) ("Hello, playground") }
result
float64
Hello, playground
We may feel strange that there is no interface here? Because in the program we can know that the variable type of f should be float32, it should not be any variable type. But we know in the golang source code that contains a variable of the type of empty interface.
func TypeOf(i interface{})Type
When we call a method, x is first stored in an empty interface, and then passed to the method as a parameter, and the method then decompresses the empty interface to obtain type information.
In the same way, get the value.
var f float64 = 13.4 ((f))
result
13.4
And there are many ways to check and modify them. A more important method is that Value has a method of type that can be returned. Another important thing is that both Type and Value provide a Kind method, which can return the word length of the stored data item (Uini, Floatr64, Slice, etc.). Similarly, the Value method also provides some methods called Int and Float to allow us to modify the stored values inside.
var f float64 = 13.44444 v := (f) (v) (()) (()) (())
result
13.444444444444445
float64
float64
13.444444444444445
There are methods like SetInt and SetFloat, but we must use them with caution.
The reflection mechanism has two important properties. First, in order to ensure the conciseness of the interface,getter
andsetter
Two methods are to accept assignments of the maximum type value, for exampleint64
Any symbolic integer can be accepted. So the value of the Int method will return aint64
value of type,SetInt
acceptint64
a value of type, so it may be converted to the actual type involved.
var x uint8 = 'x' v := (x) ("type:", ()) // uint8. ("kind is uint8: ", () == reflect.Uint8) // true. x = uint8(()) // returns a uint64.
The second feature: The interface saves the underlying type of the data item, rather than the static type. If an interface contains the value of the user-defined integer type, for example
type MyInt int var x MyInt = 7 v := (x)
Then vKind
The method call still returns, although the static type of x is MyInt. It can also be said that
Kind' won't be like
Type` treats MyInt and int as two types.
2. Values from mapping objects to interfaces
Like physical mapping, mapping in Go has its own opposites.
By usingInterface
We can use the methodRestore to the interface type, in fact, this method wraps the type and value information into the interface type and returns the value.
// Interface returns v's value as an interface{}. func (v Value) Interface() interface{}
So we can say
y := ().(float64) // y will have type float64. (y)
Printing a value of type float64 is actually a mapping of the interface type variable v.
Or we can do this,,
The parameters of the same function can run even if they are empty interface types. The method of parsing type and value in the fmt package is similar to our example above. So all printed correctly
All methods try to pass the value to the formatted printing function through the interface method.
(())
(Why not(v)
?Because by v is type.) Because our value is of type float64, we can even print in the format of floating point type.
("value is %7.1e\n", ())
turn out
3.4e+00
Therefore we do not use type assertions {} to float64 types. Because the interface type stores the value information inside, the Printf function can restore this information.
Simply put, Interface is an inverse operation of ValueOf, unless this value is always a static Interface type.
Change the interface object, its value must be variable
The third rule is more subtle and easy to confuse, but if you look at it from the first rule, it is still easier to understand.
This is an incorrect statement, but this error is worth studying
var x float64 = 3.4 v := (x) (7.1) // Error: will panic.
If you run this statement, the following error message will be reported.
panic: using unaddressable value
Because the variable v is unchangeable, the prompt value 7.1 is unaddressable. Assignable value is a feature of value, but not all values have this feature.
CanSet
Whether the method returns whether the value can be changed, for example
var x float64 = 3.4 v := (x) ("settability of v:", ())
turn out
settability of v: false
If you assign a value on a variable that cannot be assigned, an error will be caused. But what exactly can be assigned?
Assignable values are a bit like addressable, but they will be more stringent. Mapping objects can change the properties of stored values can be used to create new mapped objects. Mapping objects contain original data items is the key to determining the value that the mapped objects can be assigned. When the following code is running
var x float64 = 3.4 v := (x)
Just copy x to,therefore
The return value of 'x' is the copy term of x, not x itself. If the following statement can run normally
(5.4)
Although v seems to be created by x, the value of x will not be updated, because this statement will update the value of x copy value, but does not affect x itself. Therefore, this changeable feature is to avoid this operation.
Although this looks weird, it is actually a very familiar operation. For example, we assign x value to a method
f(x)
We don't want to modify the value of x, because what is passed is only a copy of the value of x, but if we want to modify the value of x, then we need to transfer the address of x (that is, the pointer of x)
f(&x)
This operation is simple and clear, but it is actually the same for mapping. If we want to modify the value of x by mapping, then we need to pass a pointer to x. for example
var x float64 = 3.4 p := (&x) // Note: take the address of x. ("type of p:", ()) ("settability of p:", ())
result
type of p: *float64
settability of p: false
The mapping object p is still unmodified, but in fact we do not want to modify p, but *p. In order to get the pointer, we need to useElem()
method, which will point to the value of *p and save it to the mapping variable
v := () ("settability of v:", ())
The result is
settability of v: true
Now v is a modifiable mapping object. And v represents x, so we can use()
to modify the value of x.
(7.1) (()) (x)
The output result is
7.1
7.1
Mapping is difficult to understand, although we use theValues``Types
Hidden what exactly happened. We just need to remember that if we want to change its value, then we are calling itValuesOf
A pointer to it should be used when a method.
Struct
In the previous example, v is not a pointer to itself, but is generated in other ways. Another commonly used operation is to modify a certain field of the structure. As long as we know the address of the structure, we can modify its fields.
This has an example of modifying the structure variable t. Because we want to modify the fields of the structure, we use the structure pointer to create the structure object. We use typeOfT to represent the data type of t and iterate over the fields of the structure through the NumField method. Idea: We just extract the name of the structure type field, and theirsObject.
type T struct { A int B string } t := T{23, "skidoo"} s := (&t).Elem() typeOfT := () for i := 0; i < (); i++ { f := (i) ("%d: %s %s = %v\n", i, (i).Name, (), ()) }
The output result is
0: A int = 23
1: B string = skidoo
It is worth noting that only exportable fields can be modified.
Because s contains a modifiable mapping object, we can modify the fields of the structure
(0).SetInt(77) (1).SetString("Sunset Strip") ("t is now", t)
The result is
t is now {77 Sunset Strip}
If s is created through t instead of &t, then both SetInt and SetString methods will have errors because the field of t cannot be modified.
Original blog address:The Go Blog|The Laws of Reflection
This is all about this article about the interface interface of Go language. I hope it will be helpful to everyone's learning and I hope everyone will support me more.