Hello, I'm clean!
Part I: kick you into the door of Go language!
I. the foundation is not firm and the earth is shaking
1. First example: Hello World
package main import "fmt" func main(){ fmt.Println("Hello World") }
In the first line, package main represents which package the current file belongs to. Package is the keyword of go language life package, main is the package name, and main package is a special package, which means that this project is a runnable application rather than a library referenced by other projects.
The second line, import "fmt", imports an fmt package, and import is the keyword
The third line func main() {} defines a function. Func is the keyword, main is the function name, and mian is a special function, representing the entry of the whole program. When the program is running, it will call the main function.
The fourth line fmt Println ("Hello World") prints "Hello World" text through the println function of the fmt package.
2.Go environment construction
From the official website https://golang.org/dl/ (foreign official website) and https://golang.google.cn/dl/ (domestic official website) download the Go language development kit.
2.1 environmental variables
- GOPATH: the working directory of the Go project. Now it has the Go Module mode, so it is basically used to put the projects obtained by using the go get command
- GOBIN: the program installation directory generated by go compilation. For example, the go install command will install the generated Go program into the GOBIN directory for use by the terminal.
-
If the working directory is / Users/wucs/go, set the GOPATH environment variable to / Users/wucs/go and the GOBIN environment variable to $GOPATH/bin
-
Under Linux/macOS, add the following contents to / etc/profile or $home / Save the profile file:
export GOPATH=/Users/wucs/go export GOBIN=$GOPATH/bin
3. Project structure
We use the Go Module mode for development. This mode does not need to put the code in the GOPATH directory, and projects can be created at any location.
-
For example, if the project location is \ golang\gotour, open the terminal, switch to the project directory, and then execute go mod init example COM / Hello, will generate a go Mod file. Then create main. In the project root directory Go file.
go mod is an official package dependency management tool introduced by golang version 1.11. It is used to solve the problem that there is no place to record the specific version of dependent packages before, so as to facilitate the management of dependent packages.
-
go mod init "module name" initializes the module.
-
go mod tidy add missing packages and remove useless packages
-
-
Write the Hello World instance at the beginning of the article to main Go file.
main.go is the entry file of the whole project, which contains the mian function.
4. Compile and publish
-
Execute go build in the project root directory/ main. Go, and main. Will be generated in the root directory of the project Exe file
-
In the root directory of the project, enter main at the terminal and press enter, and the "Hello World" is successfully printed, indicating that the program runs successfully.
-
The executable file generated above is in the project root directory, or it can be installed in the $GOBIN directory or any other location:
go install /main.go
The go install command can generate the program in the $GOBIN directory. Now you can open the terminal at any position, enter mian and press enter, and "Hello World" will be printed.
5. Cross platform compilation
What is cross platform compilation? For example, if you develop under windows, you can compile programs running on linux.
Go language controls cross platform compilation through two environment variables: GOOS and GOARCH.
- GOOS: represents the target operating system to be compiled. Common operating systems include Linux, Windows, Darwin, etc.
- GOARCH: represents the target processor architecture to be compiled. Common include 386, AMD64, ARM64, etc
macOS AMD64 Develop and compile under linux AMD64 Procedure: GOOS=linux GOARCH=amd64 go build ./main.go
For more combinations of GOOS and GOARCH, refer to the $GOOS and $GOARCH section of the official document.
II. Data type
1. What types are there
Variable declaration
- var variable name type = expression
var i int = 10 - Type derivation
var i = 10
Variable types can be omitted depending on the type of value - Declare multiple variables
var ( i int = 0 k int = 1 ) // Same type derivation var ( i = 0 k = 1 )
Basic types such as int/float64/bool/string can be derived automatically.
integer
In Go language, integers are divided into:
- Signed integer: int, int8, int16, int32, int64
- Unsigned integers: uint, uint8, uint16, uint32, uint64
be careful:
- Signed integers can represent negative, zero and positive numbers, while unsigned integers can only be zero and positive numbers.
- int and uint are integers without specific bit size. Their size may be 32bit or 64bit, depending on the CPU of the hardware device.
- In integer, if the bit of int can be determined, the explicit int type is used, which is helpful to the portability of the program.
- There is also a byte type, which is actually equivalent to uint8. It can be understood as an alias of uint8 type, which is used to define a byte. Therefore, byte type also belongs to integer type.
Floating point number
Floating point numbers are numbers containing decimals. Go language provides two kinds of precision floating point numbers: float32 and float64. Float 64 is more commonly used because of its high precision and smaller floating-point error than float.
Boolean
- There are only two kinds of Boolean values: true and false.
- Definition usage: var bf bool = false; Use the bool keyword to define
character string
Strings are declared by type string
var s1 string = "hello" var s2 = "world" //Type derivation var s3 = s1 + s2 //Strings can be concatenated by the operator + s1 += s2 //You can also operate with the + = operator
Zero value
Zero value is actually the default value of a variable. In Go language, if only one variable is declared and not assigned a value, the variable will have a zero value of the corresponding type.
var b bool // bool type zero value is false
Var s string / / the zero value of string is ""
The following six types of zero valued constants are nil
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error is the interface
2. Short declaration of variables
Variable name: = expression
In the actual project, if the declared variables can be initialized, the short declaration method is used, which is also the most used.
3. Pointer
In Go language, the pointer corresponds to the location of the variable stored in memory, that is, the value of the pointer is the traversal memory address. Through & you can get the address of the variable, that is, the pointer* You can get the value corresponding to the address.
pi:=&i fmt.Println(*pi)
4. Constant
Constant values are determined at compile time and cannot be modified after determination, which can prevent malicious tampering at run time.
Constant definition
And variable types, just use the keyword const
const name = "dust free"
In Go language, only Boolean, string and numeric types are allowed as constants.
5.iota
iota is a constant generator that can be used to initialize constants with similar rules to avoid repeated initialization.
const ( one = 1 two = 2 three = 3 ) //Using iota const ( one = iota+1 two three )
The initial value of iota is 0.
6. String
- String and number interchange
Go is a strongly typed language. Variables of different types cannot be used and calculated with each other. When copying or calculating variables of different types, type conversion is required first.
i := 10 itos := strconv.Itoa(i) stoi,err := strconv.Atoi(itos) fmt.Println(itos,stoi,err) //10 10 nil
- String package
The string package is a standard package provided by the Go SDK. Toolkit for handling strings. It includes finding a string, splitting a string, removing spaces from the string, and judging whether the string contains a prefix or suffix.
//Determine whether the prefix of s1 is H fmt.Println(strings.HasPrefix(s1,"H")) //Find string o in s1 fmt.Println(strings.Index(s1,"o")) //Capitalize all s1 fmt.Println(strings.ToUpper(s1))
More examples can be viewed string document
III. control structure
1. if conditional statement
func main() { i:=6 if i >10 { fmt.Println("i>10") } else if i>5 && i<=10 { fmt.Println("5<i<=10") } else { fmt.Println("i<=5") } }
be careful:
- The expression after if had no '()'
- '{}' in each conditional branch is required. Even if there is only one line of code.
3. The '{' after if / else cannot monopolize one line. Otherwise, the compilation fails
2. switch selection statement
if conditional statements are more suitable for the case of fewer branches. if there are many branches, switch will be more convenient.
switch i:=6;{ case i > 10: fmt.Println("i>10") case i > 6 && i <= 10: fmt.Println("5<i<10") default: fmt.Println("i<=5") }
Note: to prevent forgetting to write break, Go language comes with break after case, which is different from other languages.
If you really need to execute the next case, you can use the fallthrough keyword
switch j:=1;j{ case 1: fallthrough case 2: fmt.Println("1") default: fmt.Println("No match") }
The above results will output 1.
When there is an expression after switch, the value after case must be the same as the result type of the expression. For example, here j is the int type, so the int type must be used after case.
3. for loop statement
The for loop consists of three parts, of which two are required; division:
sum := 0 for i := 1; i <= 100; i++{ sum += i } fmt.Println("sum:",sum)
The first part is a simple statement
The second part is the conditions of the for loop
The third part is the update statement
These three parts are not necessary and can be omitted.
There is no while loop in Go language. You can achieve the effect of while through for:
sum := 0 i := 1 for i <= 100 { sum += 1 i++ }
Go also supports continue and break to control for loops.
- continue jumps out of this cycle and enters the next cycle.
- break forcibly exits the entire loop.
IV. set type
1. Array
Arrays store data of the same type with a fixed length.
1.1 array declaration
-
Var < array name > = [< len gt h >] < element > {element 1, element 2}
var arr = [2]int{1,2}
perhaps
arr := [2]int{1,2} -
Var < array name > = [...] < element type > {element 1, element 2}
var arr = [...]int{1,2}
perhaps
arr := [...]int{1,2} -
Var < array name > = [...] < type > {index 1: element 1, index 2: element 2}
var arr = [...]int{1:1,0:2}
perhaps
arr := [...]int{1:1,0:2}
Each element of the array is stored continuously in memory. Each element has a subscript, which starts from 0.
The array length can be omitted and will be automatically deduced according to the elements in {}.
There is no initialized index. The default value is the zero value of array type.
1.2 array loop
for i,v := range array { fmt.Printf("Indexes:%d,Value:%s\n",i,v) }
- The range expression returns the array index assigned to i and the array value assigned to v.
- If the returned value cannot be used, it can be used_ Underline discard:
for _,v:= range array{ fmt.Printf("Value:%s\n",i,v) }
2. Slice
Slice and array types can be understood as dynamic arrays. Slice is implemented based on arrays, and its bottom layer is an array. For the segmentation of the array, you can get a slice.
2.1 array generation slice
slice := array[start:end]
array := [5]string{"a","b","c","d","e"} slice := array[2:5] fmt.Println(slice) //[c d e]
Note: index 2 is included here, but the elements of index 5 are not included, i.e. closed on the left and open on the right.
After slicing, the index range of the slice also changes.
Both start and end in array[start:end] can be omitted. The default value of start is 0 and the default value of end is the length of the array.
array[:] Equivalent to array[0:5]
2.2 slice modification
The value of the slice can also be modified. It can also be proved that the bottom layer of the slice is an array.
array := [5]string{"a","b","c","d","e"} slice := array[2:5] //[c d e] slice[1] = "f" fmt.Println(slice) //[c f e] fmt.Println(array) //[a b c f e]
When the slice is modified, the corresponding array value is also modified. Therefore, it is proved that the underlying array used by the array based slice is still the original array. Once the element value of the slice is modified, the corresponding value of the underlying array will also be modified.
2.3 slicing statement
Declare slices using the make function
//Declare a slice whose element type is string and its length is 4 slice := make([]string,4) //The length is 4 and the capacity is 8 slice1 := make([]srting,4,8)
The capacity of the slice cannot be smaller than the length of the slice.
Length is the number of elements.
Capacity is the space of the slice.
The above example divides a memory space with a capacity of 8, but only 4 memory spaces are used, and the rest are idle. When an element is added to the slice through append, it will be added to the free memory. When the remaining space is insufficient, it will be expanded.
Literal initialization slice
slice2 := []string{"a","b","c"} fmt.Println(len(slice2),cap(slice2)) //3 3
2.3 Append
The append function appends elements to a slice:
slice3 := append(slice2,"d") //Append multiple elements slice3 := append(slice2,"d","f") //Append a slice slice3 := append(slice2,slice...)
Tips:
When creating a new slice, it is best to make the length and capacity the same, so that a new underlying array will be generated during the append operation, which will be separated from the original array, so that multiple slices will not be affected when modifying the content due to the common underlying array.
2.4 slice cycle
Like arrays, slice loops use the for range method.
3. Map
A map is an unordered collection of k-v key value pairs. Where k must be of the same type. The types of K and v can be different. The type of K must support the = = comparison operator, so as to judge whether it exists and ensure its uniqueness.
3.1 Map declaration initialization
-
make:
mapName := make(map[string]int) -
Literal:
Mapname: = map [string] int {"clean": 29}
If you don't want to add key value pairs when creating them, you can use empty braces {} and remember not to omit them.
3.2 Map acquisition and deletion
//Add a key value pair or update the value of the corresponding key mapName["Dust free"] = 20 //Gets the value of the specified key age := mapName["Dust free"]
When obtaining a nonexistent k-v key value pair, if the key does not exist, the returned value is the zero value of the value. Therefore, it is often necessary to judge whether the key in the map exists.
nameAge := make([string]int) nameAge["Dust free"]=29 age,ok := nameAge["Dust free"] if ok { fmt.Println(age) }
- The [] operation of map returns two values
- The first is value
- The second is to mark whether the key exists. If it exists, it is true
delete() function
delete(nameAge, "dust free")
- Delete has two parameters, one is map and the other is key to delete.
4. Traverse Map
nameAge["Dust free"] = 29 nameAge["Dust free 1"] = 30 nameAge["Dust free 2"] = 31 for k,v := range nameAge{ fmt.Println("key is",k,"value is ",v) }
- Corresponding to map, for range returns two parameters, k and v.
Tip: when for range traverses a map, if a return value is used, the return value is the key of the map.
4.1 Map size
Map is different from slice. Map has only length and no capacity. You can use the len function to get the map size.
5. String and [] byte
String is also an immutable byte sequence, which can be directly converted into byte slice [] byte:
s:="Hello Dust free life" bs := []byte(s)
Not only can string be directly converted to [] byte, but also the [] operator can be used to obtain the byte value of the specified index.
String is a sequence of bytes. Each index corresponds to one byte. Under UTF8 coding, a Chinese character corresponds to three bytes.
If you calculate a Chinese character as a length, you can use utf8 Runecountinstring function.
When traversing for range, it loops according to unicode characters, and one Chinese character accounts for one length.
V. functions and methods
1. Function
1.1 function declaration
func funcName(params) result { body }
- The keyword func is used to declare a function
- funcName function name
- Parameters of params function
- result is the return value of the function. Multiple return values can be returned. If not, it can be omitted.
- Body function body
Example
1.
- a. b the formal parameter types are consistent, and the declaration of one type can be omitted
func sum (a, b int) { return a + b }
2. Multi value return
- Part of the type definition of the return value needs to be enclosed in parentheses.
func sum (a, b int) (int,error) { if a <0 || b <0 { return 0, errors.New("a or b Cannot be negative") } return a + b, nil }
3. Named parameter return
- Assigning a value to the named return parameter in the function is equivalent to the return value of the function, so the value to be returned after return can be ignored.
func sum (a, b int) (sum int,err error) { if a <0 || b <0 { return 0, errors.New("a or b Cannot be negative") } sum = a + b err = nil return }
4. Variable parameters
- The arguments to the function are mutable
- To define a variable parameter, just add three points... Before the parameter type
- The type of variable parameter is actually slice. In the following example, the parameter type of params is [] int
func sum(params ...int) int { sum := 0 for _, i := range params { sum += i } return sum }
1.2 package level functions
- All functions belong to a package, and our custom functions belong to the main package. The Println function belongs to the fmt package.
- If you want to call a function in another package, the first letter of the function name should be capitalized to make its scope public.
- The function is lowercase and can only be called in the same package
1.3 anonymous functions and closures
An anonymous function is a function without a name.
func main(){ //Note that sum is just a function type variable, not a function name sum := func(a, b int) int { return a + b } fmt.Println(sum(1, 2)) // 3 }
Anonymous functions can be nested in functions. This anonymous function is called an internal function. Internal functions can use variables of external functions. This method is closure.
func main (){ sm := sum() fmt.Println(sum()) fmt.Println(sum()) fmt.Println(sum()) } func sum () func() int{ i := 0 return func ()int{ i++ return i } } //The result is: 1 2 3
Because the closure function and sum function return an anonymous function, which holds the variable i of the external function sum, the value of i will be + 1 every time sum() is called in the main function.
In Go language, function is also a type, which can be used as a variable, parameter or return value of a function.
2. Method
Methods are similar to functions, except that they must have a receiver, which is a "class" (type), so that the method belongs to this "class".
type Name string func (n Name)String(){ fmt.Println("name is ", n) }
- In the example, String() is a method of type Name
- The receiver needs to be added between func and method name, using ()
- Receiver: (variable, type)
use:
func main(){ name := Name("Dust free") name.String() } //source name is Dust free
3. Value type receiver and pointer type receiver
The receiver of the method can use a value type (such as the example above) or a pointer type.
If the receiver is a pointer, the modification of the pointer is valid:
func (n *Name) Modify(){ *n = Name("wucs") } func main(){ name := Name("Dust free") name.String() name.Modify() name.String() } //output name is Dust free name is wucs
Note: when a method is called, the recipients passed are essentially copies, but one is a copy of the value and the other is a copy of the pointer to the value. The pointer points to the original value, so modifying the value pointed to by the pointer will modify the original value.
Method can be a value or a pointer (& name) Modify (), Go language will escape automatically, we don't need to care.
4. Method expression
Method can be assigned to a variable
name := Name("Dust free") //Method assignment to variable, method expression n = Name.String //To send a receiver name for calling n(name)
Whether the method has parameters or not, when called through a method expression, the first parameter must be the receiver, and then the parameter of the method itself.
Vi. struct and interface
1. Structure
1.1 definitions
A structure is an aggregate type, which can contain any type of values. These values are members of the structure or become fields. To define the structure, you need to use the keyword combination of type+struct
type person struct { //Human structure name string //People's names age uint //People's age }
- Type and struct are keywords used to define the type of a new structure.
- person is the structure name.
- name/age is the field name of the structure, followed by the corresponding field type.
- Field declarations are similar to variables, with variable names first and type last
- A field can be a person or a structure without a field, which becomes an empty structure.
- Structure is also a type. For example, person structure and person type mean the same thing.
1.2 declaration
- Like normal string, integer, hospital declaration initialization
var p person
A person type variable p is declared, but it is not initialized, so the zero value of the field in the structure is used by default.
- Literal initialization
P: = person {"clean", 18}
Indicates that the name field of the structure variable p is initialized to "clean", and the age field is initialized to 18. The order must be consistent with the field definition order.
- Initialize by field name
P: = person {age: 18, name: "clean"}
By pointing out field names like this, you can disrupt the order of initializing fields. You can also initialize only some of the fields, and the remaining fields use zero values by default: P: = person {age: 30}
1.3 field structure
The structure field can be any type, including custom structure types:
type person struct { //Human structure name string age uint addr address //Using custom structure types } type address struct { //Address structure city string }
For such a nested structure, the initialization is similar to that of a general structure. It can be initialized according to the type corresponding to the field:
p := person { age:18, name:"Dust free", addr:address{ city:"Beijing", }, }
Like calling a type of method, the fields of a structure use the dot operator ".":
fmt.Println(p.age) //To access the value of the city field in the nested structure: fmt.Println(p.addr.city)
2. Interface
2.1 definitions
An interface is an abstract type and a convention with the caller. The interface only needs to define a convention to tell the user what to do without knowing its internal implementation.
The definition of interface is type + interface keyword class implementation.
//Info is an interface with the method Getinfo()string type Info interface { Getinfo() string }
Corresponding to the Stringer interface, it will tell the caller that a string can be obtained through String(), which is the Convention of the interface. The interface doesn't care about how to obtain this string, and the caller doesn't care, because these are handled by the implementer of the interface.
2.2 implementation of interface
The implementer of the interface must be a specific type:
func (p person) Getinfo() string { return fmt.Sprintf("my name is %s,age is %d",p.name,p.age) }
- A method is defined for the structure type person. The name, parameters and return values of this method are the same as those in the interface, which means that this structure person implements the Info interface.
- If an interface has more than one method, it is necessary to implement all the methods in the interface.
2.3 use
Let's first define a function that can print the Info interface:
func printInfo(i Info) { fmt.Println(i.Getinfo()) }
- Define the function pringInfo, which receives an Info interface type parameter, and then prints the string returned by the interface Getinfo method.
- The pringInfo function here is interface oriented programming. Only any type implements the Info interface. You can use this function to print the corresponding string without paying attention to the specific type implementation.
printInfo(p) //The result is: my name is dust-free, age is 18
Because the person type implements the Info interface, the variable p can be used as the parameter of the function printInfo.
3. Value receiver and pointer receiver
- To implement an interface, all methods in the interface must be implemented.
- Define a method with a value type receiver and a pointer type receiver. Both can call the method because the Go compiler automatically converts it.
- However, the receiver of value type is different from that of pointer type in the implementation of interface
The above interface body person implements the Info interface. Does the structure pointer also implement the interface?
printInfo(&p)
The test shows that the pointer of p can also operate normally as a parameter function, indicating that the receiver of value type implements the interface, and the type itself and the pointer type of this type implement the interface
Then change the receiver to pointer type:
func (p *person) Getinfo() string { return fmt.Sprintf("my name is %s,age is %d",p.name,p.age) }
Then call the function printInfo(p). If the code fails to compile, it indicates that the receiver of the pointer type implements the interface. Only the corresponding pointer type is considered to implement the interface
Method recipient | Implementation interface type |
---|---|
(p person) | Person and * person |
(p *person) | *person |
- When the value type is the receiver, both person type and * person type implement the interface.
- When the pointer type is the receiver, only the * person type implements the interface.
VII. Error handling, error and panic
1. Error
In the Go language, the error is not very serious. It can be expected and can return the error to the caller for processing.
1.1 error interface
In Go language, errors are represented through the built-in error interface. It has only one error method to return error information:
type error interface { Error() string }
Here is an example of an error:
func main() { i,err := strconv.Atoi("a") if err != nil { fmt.Println(err) }else { fmt.Println(i) } }
- The example deliberately uses the wrong string "a" to convert to an integer, so the error message will be printed here:
strconv.Atoi: parsing "a": invalid syntax - Generally, the error interface returns an error when a function or method is called, and it is the second return value, so that the caller can handle it according to the error.
1.2 error factory function
We can use errors New is a factory function to generate error messages. It receives a string parameter and returns an error interface.
func test(m,n int) (int, error) { if m > n { return m,errors.New("m greater than n") }else { return n,nil } }
When m is about n, an error message is returned.
1.3 custom error
The above factory function can only pass one string to return. To carry more information, you can use custom error:
type testError struct { errorCode int //Error code errorMsg string //error message } func (t *testError) Error() string{ return t.errorMsg }
Here you can customize error, which can return more information:
return m, &testError{ errorCode: 1, errorMsg: "m greater than n"}
The above creates * testError by literal method to return.
1.4 error assertion
Obtain the returned error information through the error assertion. The assertion can convert the error interface to its own defined error type:
res, err := test(2,1) if e,ok := err.(*testError);ok { fmt.Println("Error code:",e.errorCode,",Error message:",e.errorMsg) } else { fmt.Println(res) }
2. Panic exception
Go language is a static language. Many errors can be caught during compilation. However, for array cross-border access and different types of forced conversion, panic exceptions will be caused at runtime.
We can also throw panic exceptions manually. Here, take connecting to mysql database as an example:
func connectMySQL(ip,username,password string){ if ip =="" { panic("ip Cannot be empty") } //Omit other codes }
- In the above function, if the ip address is empty, a panic exception will be thrown.
- Panic is a built-in function of Go language and can receive parameters of interface {} type, that is, any type of value can be passed to panic function:
func panic(v interface{})
Interface {} represents an empty interface and represents any type.
panic is a very serious error that will interrupt the execution of the program, so if it is not an error that affects the operation of the program, use error
2.1 Recover captures Panic exceptions
Generally, we do not handle panic exceptions, but if there are some operations that need to be handled before the program crashes, you can use the built-in recover function to recover panic exceptions.
When the program panic crashes abnormally, only the function modified by defer will be executed, so the recover function should be used together with the defer keyword:
func main() { defer func() { if p:=recover();p!=nil{ fmt.Println(p) } }() connectMySQL("","root","123456") }
The recover function captures the panic exception. Print: the value returned by the recover function is the parameter value passed through the panic function. ip address cannot be empty
- The return value of the recover function is the parameter value passed by the panic function.
- The function modified by the defer keyword will be executed before the main function exits.
VIII. Assertion and reflection
1. Interface assertion
When it comes to interface assertions, let's first review how to implement interfaces?
- The implementer of an interface must be a concrete type
- The method name, parameter and return value of the type defined method and the interface must be consistent
- If the interface has multiple methods, implement all the methods in the interface
For the empty interface interface {}, because it does not define any function (method), all types in Go implement the empty interface.
When the formal parameter of a function is interface {}, it means that the parameter is automatically converted to interface {} type. In the function, if you want to get the real type of the parameter, you need to assert the formal parameter.
- Type assertion is to convert the value x of interface type into type T in the format of x.(T)
- Type assertion x must be an interface type
- T can be a non interface type. If you want to assert legally, t must implement the interface of x
1.1 syntax format:
//Unsafe type assertion <Value of target type> := <expression>.( Target type ) // Security type assertion <Value of target type>,<Boolean parameters > := <expression>.( Target type )
Example
package main import "fmt" func whoAmi(a interface{}) { //1. Keep talking //Program error: cannot convert a (type interface{}) to type string: need type assertion //fmt.Println(string(a)) //2. Unsafe type assertion //fmt.Println(a.(string)) / / dust free //3. Security type assertion value, ok := a.(string) //If the assertion fails, it will not panic, but the value of ok is false if !ok { fmt.Println("assertion failure ") return } fmt.Println(value) //Dust free } func main() { str := "Dust free" whoAmi(str) }
Another form of assertion is to use the switch statement to judge the type of interface:
func whoAmi(a interface{}) { switch a.(type) { case bool: fmt.Printf("boolean: %t\n", a) // a has type bool case int: fmt.Printf("integer: %d\n", a) // a has type int case string: fmt.Printf("string: %s\n", a) // a has type string default: fmt.Printf("unexpected type %T", a) // %T prints whatever type a has } }
2. Reflection
Go language provides a mechanism to update and check the value of variables, the methods of calling variables and the internal operations supported by variables at run time, but the specific types of these variables are not known at compile time. This mechanism is called reflection.
2.1 what is the use of reflection
- Above we mentioned the empty interface, which can receive anything
- But how to judge what type of empty interface variable is stored? The type assertions described above can be implemented
- If you want to get the type information and value information of storage variables, you need to use reflection
- Reflection is a mechanism that can dynamically obtain variable type information and value information
2.1 reflect package
Reflection is supported by the reflect package, which provides two types to access the contents of interface variables, namely, Type and Value.
The reflect package provides two functions to get the Type and Value of any object:
- func TypeOf(i interface{}) Type
- func ValueOf(i interface{}) Value
function | effect |
---|---|
reflect.TypeOf() | Gets the type information of the variable. If it is empty, nil is returned |
reflect.ValueOf() | Gets the value of the data. If it is empty, it returns 0 |
Example:
package main import ( "fmt" "reflect" ) func main() { var name string = "Wechat bird's nest" // TypeOf will return the type of variable, such as int/float/struct / pointer, etc reflectType := reflect.TypeOf(name) // valueOf returns the value of the variable, which is "wechat bird's nest" reflectValue := reflect.ValueOf(name) fmt.Println("type: ", reflectType) //type: string fmt.Println("value: ", reflectValue) //value: wechat bird's nest }
- Return value of function TypeOf reflect Type is actually an interface that defines many methods to obtain type related information:
type Type interface { // All types can call the following functions // The number of bytes occupied by this type of variable after alignment Align() int // If it is a field of struct, the number of bytes occupied after alignment FieldAlign() int // The 'i' (passed in parameter) method in the return type method set Method(int) Method // Get method by name MethodByName(string) (Method, bool) // Gets the number of methods exported from the type method set NumMethod() int // Type name Name() string // Return the path of the type, such as encoding/base64 PkgPath() string // Return the size of the type, and unsafe Sizeof functions similarly Size() uintptr // Returns the string representation of the type String() string // Returns the type value of the type Kind() Kind // Does the type implement the interface u Implements(u Type) bool // Can it be assigned to u AssignableTo(u Type) bool // Can type be converted to u ConvertibleTo(u Type) bool // Can types be compared Comparable() bool // The following functions can only be called with specific types // For example, the two methods of key and element can only be called if they are of Map type // Number of digits occupied by type Bits() int // The direction of the return channel can only be called by chan type ChanDir() ChanDir // Whether the return type is a variable parameter can only be a func type call // For example, t is the type func (x, int, y... Float64) // Then t.IsVariadic() == true IsVariadic() bool // Returns the internal sub element type, which can only be called by the types Array, Chan, Map, Ptr, or Slice Elem() Type // Returns the ith field of structure type, which can only be called by structure type // If i exceeds the total number of fields, panic will occur Field(i int) StructField // Returns the fields of a nested structure FieldByIndex(index []int) StructField // Get field by field name FieldByName(name string) (StructField, bool) // FieldByNameFunc returns the struct field with a name // Returns a field whose name matches the func function FieldByNameFunc(match func(string) bool) (StructField, bool) // Gets the type of the ith parameter of the function type In(i int) Type // The key type of the returned map can only be called by the map type Key() Type // Returns the length of Array, which can only be called by type Array Len() int // Returns the number of type fields, which can only be called by type Struct NumField() int // Returns the number of input parameters of the function type NumIn() int // Returns the number of return values of the function type NumOut() int // Returns the type of the ith value of the function type Out(i int) Type // Returns the same part of the type structure common() *rtype // Returns different parts of the type structure uncommon() *uncommonType }
- Return value of function TypeOf reflect Value is a struct type. The value structure defines many methods that can directly manipulate the actual data pointed to by the value field ptr:
// Set the len field of the slice. If the type is not slice, it will be panic func (v Value) SetLen(n int) // Set the cap field for the slice func (v Value) SetCap(n int) // Set kv for dictionary func (v Value) SetMapIndex(key, val Value) // Returns the value at index i of slice, string, array func (v Value) Index(i int) Value // Gets the internal field value of the structure by name func (v Value) FieldByName(name string) Value // ......
struct reflection example:
package main import ( "fmt" "reflect" ) type Address struct { City string } type Person struct { Name string Age uint Address // Anonymous field } func (p Person) Hello(){ fmt.Println("I'm clean") } func main() { //P: = person {Name: "no dust", Age:18,Address:Address{City: "Beijing"}} / / map assignment p := Person{"Dust free",18,Address{"Beijing"}} // Get target object t := reflect.TypeOf(p) fmt.Println("t:", t) // . Name() can get the name of this type fmt.Println("Name of the type:", t.Name()) // Gets the value type of the target object v := reflect.ValueOf(p) fmt.Println("v:", v) // . NumField() gets the total number of fields it contains for i := 0; i < t.NumField(); i++ { // Get the key contained in Person from 0 key := t.Field(i) // interface method to get the value corresponding to the key value := v.Field(i).Interface() fmt.Printf("The first%d The first fields are:%s:%v = %v \n", i+1, key.Name, key.Type, value) } // Take out the details of the City and print it out fmt.Printf("%#v\n", t.FieldByIndex([]int{2, 0})) // . NumMethod() to get the method in Person for i:=0;i<t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("The first%d The first method is:%s:%v\n", i+1, m.Name, m.Type) } }
Operation results:
t: main.Person Name of the type: Person v: {Dust free 18 {Beijing}} The first field is: Name:string = Dust free The second field is: Age:uint = 18 The third field is: Address:main.Address = {Beijing} reflect.StructField{Name:"City", PkgPath:"", Type:(*reflect.rtype)(0x4cfe60), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false} The first method is: Hello:func(main.Person)
- Modify content by reflection
package main import ( "reflect" "fmt" ) type Person struct { Name string Age int } func main() { p := &Person{"Dust free",18} v := reflect.ValueOf(p) // The modified value must be a pointer type if v.Kind() != reflect.Ptr { fmt.Println("It is not a pointer type and cannot be modified") return } // Gets the element pointed to by the pointer v = v.Elem() // Gets the encapsulation of the Value of the target key name := v.FieldByName("Name") if name.Kind() == reflect.String { name.SetString("wucs") } fmt.Printf("%#v \n", *p) // If it's an integer test := 666 testV := reflect.ValueOf(&test) testV.Elem().SetInt(999) fmt.Println(test) }
Operation results:
main.Person{Name:"wucs", Age:18} 999
- Calling methods through reflection
package main import ( "fmt" "reflect" ) type Person struct { Name string Age int } func (p Person) EchoName(name string){ fmt.Println("My name is:", name) } func main() { p := Person{Name: "Dust free",Age: 18} v := reflect.ValueOf(p) // Obtain method control // Official explanation: Value encapsulation in the form of a function that returns the bound (Value held by v) state of the method named name of v mv := v.MethodByName("EchoName") // Pieced parameter args := []reflect.Value{reflect.ValueOf("wucs")} // Call function mv.Call(args) }
Operation results:
My name is: wucs
Part II: efficient concurrent programming example of Go
- This time I will introduce you to the basics of go programming. The next section of concurrent programming will be introduced later. Thank you for watching.
- Welcome to leave a message for communication
- My wechat wucs_dd, the official account of "micro guest nest", focuses on go development and technology sharing.