Kick you into the Go language gate! Beginners must read, 10000 words long text, it is recommended to collect!

Posted by RazorICE on Thu, 20 Jan 2022 05:36:02 +0100

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.
  1. 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

  2. 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.

  1. 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

  2. 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

  1. Execute go build in the project root directory/ main. Go, and main. Will be generated in the root directory of the project Exe file

  2. 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.

  3. 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

  1. var variable name type = expression
    var i int = 10
  2. Type derivation
    var i = 10
    Variable types can be omitted depending on the type of value
  3. 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:

  1. Signed integers can represent negative, zero and positive numbers, while unsigned integers can only be zero and positive numbers.
  2. int and uint are integers without specific bit size. Their size may be 32bit or 64bit, depending on the CPU of the hardware device.
  3. 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.
  4. 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

  1. 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
  1. 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:

  1. The expression after if had no '()'
  2. '{}' 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.

  1. continue jumps out of this cycle and enters the next cycle.
  2. 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

  1. Var < array name > = [< len gt h >] < element > {element 1, element 2}
    var arr = [2]int{1,2}
    perhaps
    arr := [2]int{1,2}

  2. Var < array name > = [...] < element type > {element 1, element 2}
    var arr = [...]int{1,2}
    perhaps
    arr := [...]int{1,2}

  3. 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)
}
  1. The range expression returns the array index assigned to i and the array value assigned to v.
  2. 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

  1. make:
    mapName := make(map[string]int)

  2. 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
}
  1. Type and struct are keywords used to define the type of a new structure.
  2. person is the structure name.
  3. 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

  1. 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.

  1. 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.

  1. 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

  1. To implement an interface, all methods in the interface must be implemented.
  2. 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.
  3. 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 recipientImplementation 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:

  1. func TypeOf(i interface{}) Type
  2. func ValueOf(i interface{}) Value
functioneffect
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
}
  1. 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
}
  1. 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)
  1. 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
  1. 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.

Topics: Go