1 the Go study

1.1 Go language features

  • Automatic garbage collection
  • Richer built-in types
  • The function returns multiple values
  • Error handling
  • Anonymous functions and closures
  • Types and interfaces
  • Concurrent programming
  • reflection
  • Language interactivity

1.2 Language Structure

  • Package declaration
  • Introduction of package
  • function
  • variable
  • Statement & expression
  • annotation
package main

import "fmt"

func main() {
	/* Always Hello, World! */
	fmt.Println("Hello, World!")
}

Copy the code

1.2.1 explanation:

    1. Package main defines the package name. You must specify which package the file belongs to in the first non-comment line of the source file. Package Main represents a program that can be executed independently, and each Go application contains a package named Main
    1. Import “FMT” tells the compiler that the program needs the FMT package to run
    1. Func main() is the function that the program starts executing. The main function, which every executable must include, is generally the first function to be executed after startup (or init() if there is one).
    1. {} cannot contain a single line of “{“.
    1. Comments are ignored during program execution. // Single-line comment, /.Multiline comments, also known as block comments, cannot be nested and are generally used to describe package documentation or comment block code snippets.
    1. fmt.Println(…) Prints the string to the console with the newline character \n automatically appended at the end. Use FMT. Print (” Hello, World! \n”) can get the same result.

1.2.2 test init

In addition to the main function, there is another special init function that precedes the main function to perform some package-level initialization.

The init function does the following:

  • Initialize a variable that cannot be initialized with an initialization expression.
  • Registration before the program runs.
  • Implement the sync.once function.
  • other

Key features of the init function:

  • The init function is automatically executed before main and cannot be called by other functions.
  • The init function takes no input arguments and returns no values.
  • Each package can have multiple init functions;
  • A package can also have multiple init functions per source file, which is special;
  • Golang does not specify the sequence of init execution in the same package. It is important to ensure that the program does not rely on this sequence.
  • The order in which the init functions of different packages are executed depends on the package import dependencies.

Golang program initialization

The golang program is initialized before the main function, and is initialized by Runtime in the following order:

    1. Initialize imported packages (packages are initialized in a different order than the import order. The Runtime resolves package dependencies. Packages without dependencies are initialized first, similar to variable dependencies).
  • Initialize package-scoped variables (variables in this scope are initialized according to runtime resolution variable dependencies, with non-dependent variables initialized first)
Package main import "FMT" func init() {// initialize function FMT.Println(" initialize function ")} func main() {/* Always Hello, World! */ fmt.Println("Hello, World!" )}Copy the code

Initialization order: variable initialization ->init()->main()

1.3 Development Environment Configuration

1.3.1 IDE choice

Vscode (free) : code.visualstudio.com/ goland (not free) : blog.jetbrains.com/go/

1.3.2 Installing the Go language Package

Download address: studygolang.com/dl, select according to your own system version

1.3.2.1 configuration

  • Regardless of the system, you need to configure the GO installation environment into the PATH environment variable.
  • Goland only needs to select the SDK configuration for Go, that is, add the go installation path, and compile using the installed GO environment without installing any additional plug-ins.
  • Vscode needs to install additional plug-ins, directly search go in the plug-in market, and then install. It is easy to download failure during installation, so you need to change to domestic source, as follows:

Requires Go version >=1.13 Windows Settings:

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
Copy the code

MacOS or Linux

export GO111MODULE=on
export GOPROXY=https://goproxy.cn
Copy the code

1.3.2.2 Installation on Windows

Double-click the installation package you downloaded (go1.13.4.windows-amd64.msi) and keep clicking “Next”. Note that there is a step that requires you to select the installation path.

 

Then click “Install” to Install

 

Please wait for this process

Click “Finish” to complete the installation

How do we verify that our installation is successful? Press the Windows key +R, enter CMD, and press Enter

In the command line window that appears, enter: Go Version press enter, then the go version you currently installed will be displayed, indicating that we have successfully installed

1.3.3 installation VSCode

Download 1.3.3.1

Open the website code.visualstudio.com/Download, enter…

Win10 64 bit click on the first red arrow to download, CentOS 64 bit click on the second arrow to download

1.3.3.2 Installation on Windows

Double click on the installer you downloaded and click along the way in the following order

 

 

 

 

 

1.3.3.3 Installing VSCode Packages

If you are like the author, you can choose to install the Chinese language pack

Click on the fifth icon on the left, or press Ctrl+Shift+X to open Extensions. Type: Language in the Search box and then click “Install”

Open VSCode again, it is already in Chinese

1.3.3.4 Installing the Go plug-in

Open Extensions and type Go to install this from author Microsoft

1.3.3.4 installation vscode – go

For Visual Studio Code development tools, there is an excellent GoLang plugin at github.com/microsoft/v… The features of this plug-in include:

Colorization code with colored Completion Lists automatically completed (using goCode) Snippets Quick Info Quick prompt information (using godef) Goto Definition Skip to definition (use godef) Find References Search reference (use go-find-references) File Outline File outline (use go-Outline) Workspace symbol Search Workspace symbols search (using Go-symbols) Rename Rename (using gorename) build-on-save Save Build (using Go Build and Go Test) Format Formatting (use goreturns or Goimports or gofmt) Add Imports Add references (use gopkgs) Debugging Debug code (use delve) integrate installation commands and copy them to CMD to complete installation: go get -u -v github.com/nsf/gocode go get -u -v github.com/rogpeppe/godef go get -u -v github.com/golang/lint/golint go get -u -v github.com/lukehoban/go-find-references go get -u -v github.com/lukehoban/go-outline go get -u -v sourcegraph.com/sqs/goreturns go get -u -v golang.org/x/tools/cmd/gorename go get -u -v github.com/tpng/gopkgs go get -u -v github.com/newhook/go-symbols

1.4 Test Installation

1, in GoPath create a new folder GoHello, open VSCode to open this folder, create a new file main.go, enter the following code in the file

package main
 
import (
    "fmt"
)
 
func main() {
    fmt.Println("hello world")
}
Copy the code

If there is a message in the lower right corner indicating that some plug-ins need to be updated, you can ignore it, or click the update button directly, as shown in the picture: Your version of go-outline appears to be out of date. Please update for an improved experience.

2 Go Data type, keyword, and identifier

2.1 Data Types

2.1.1 by category

  • Boolean: Can only be constants true or false.
//eg:
var b bool = true
Copy the code
  • Numeric types: integer and floating point.

  • Bit operations use the complement string type: a string is a sequence of fixed length characters concatenated, and a Go string is a single byte concatenated.

  • The bytes of strings in the Go language use utF-8 encoding to identify Unicode text

  • Complex numbers: complex128 (64-bit real and imaginary numbers) and complex64 (32-bit real and imaginary numbers), where complex128 is the default type. Note:

  • I. The value of the complex number consists of three parts RE + IMi, where RE is the real part and IM is the imaginary part. Both RE and IM are float types, and the last I is an imaginary unit.

Var name complex128 = complex(x, y) x = real(z) y = imag(z)Copy the code
  • == == =! = For equality comparison, two complex numbers are equal only if their real and imaginary parts are equal

2.1.2 Derived Types

  • Pointer type
  • An array type
  • Structured type (struct)
  • The Channel type
  • Function types
  • Slice type
  • Interface Type
  • The Map type

2.1.3 Based on architecture

Integer, and provides four signed integer types, corresponding to 8, 16, 32, and 64bit (binary) signed integers respectively, and corresponding to four unsigned integer types

  • Uint8: Unsigned 8-bit integer (0~255)
  • uint16
  • uint32
  • uint64
  • int8
  • int16
  • int32
  • int64

Floating point Numbers

  • float32
  • float64
  • Complex64 (real and imaginary)
  • complex128

other

  • byte
  • rune
Package main import (" FMT ""unicode/utf8") func main() {var STR = "hello hello" Println("len(STR):", Len (STR)) // The unicode/utf8 package in Golang provides a utF-8 method to get the length of STR fmt.Println("RuneCountInString:", Println("rune:", len([]rune(STR)))} utf8.runecountinString (STR)) // Handle Unicode characters with the rune type fmt.println ("rune:", len([]rune(STR)))}Copy the code

Return result:

len(str): 12
RuneCountInString: 8
rune: 8
Copy the code
  • uint
  • int
  • Uintptr (unsigned integer, store a pointer)

Note:

    1. The rune type representing Unicode characters is equivalent to the INT32 type, which is usually used to represent a Unicode character, and is equivalent.
  • 2. Byte and uint8 are also equivalent. The byte type is generally used to emphasize that a value is a raw data rather than a small integer.
    1. Uintptr unsigned integer type that does not specify a specific bit size but is large enough to hold Pointers. This is only necessary for low-level programming, especially where Go interacts with C libraries or operating system interfaces.
    1. The signed integer is represented by the complement of 2, that is, the highest bit is used to represent the signed bit. The value of the signed number of an n-bit ranges from -2(n-1) to 2(n-1)-1. All bits of an unsigned integer are used to represent non-negative numbers, ranging from 0 to 2n-1.
    1. The constant math.MaxFloat32 indicates the largest value that float32 can take, which is about 3.4e38.
    1. Constant math.MaxFloat64 indicates the maximum value that float64 can take, which is about 1.8E308
    1. The minimum values of FLOAT32 and float64 are 1.4E-45 and 4.9E-324 respectively.
    1. Floating-point numbers can be declared as either integers or decimals.
Const e =.71828 //0.71828 const f = 1Copy the code
    1. Small or large numbers are best written in scientific notation, specifying the exponential part by e or e
Const Avogadro = 6.02214129e23 // Avogadro constant const Planck = 6.62606957E-34 //Copy the code

2.2 the keyword

2.2.1 25 keywords or reserved words

The keyword The keyword The keyword The keyword The keyword
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

2.2.2 36 Predefined identifiers

Predefined identifiers Predefined identifiers Predefined identifiers Predefined identifiers Predefined identifiers Predefined identifiers
append bool byte cap close complex
complex64 complex128 uint16 copy false float32
float64 imag int int8 int16 uint32
int32 int64 iota len make new
nil panic uint64 print println real
recover string true uint uint8 uintptr

2.2.3 knowledge

  • Programs generally consist of keywords, constants, variables, operators, characters, and functions.
  • Programs may use these delimiters: parentheses (), brackets [], and braces {}.
  • These punctuation marks may be used in the program:.,,,;. And, and… .

2.3 the identifier

Identifiers are used to name variables, types, and other program entities. An identifier is actually A sequence of one or more letters (A, Z and A, Z) digits (0 to 9) and underscores (_), but the first character must be A letter or underscore rather than A number.

3 Go variables, constants, enumerations

3.1 variable

Variable: An abstract concept that can store the result of a calculation or represent a value. The value can be accessed using variable names, which consist of letters, digits, and underscores (_). The first character cannot be a digit.

A common form of declaring a variable is to use the var keyword:

var identifier type
var identifier1, identifier2 type
Copy the code

Variable declaration:

    1. Value types (including complex64/128) default to zero, bool default to false, string default to “”, 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 interface “default nil
    1. You can determine the type based on the value
    1. V_name := value (); v_name := value ()
    1. Multivariable declarations:
Vname1, vname2, vname3 type vname1, vname2, vname3 = v1, v2, v3 var vname1, vname2, vname3 = v1, Vname1, vname2, vname3 := v1, v2, v3 // The variable to the left of := cannot be declared, Var (vname1 v_type1 vname2 v_type2) var (vname1 v_type1)Copy the code

Note:

  1. The “:=” assignment operator efficiently creates a new variable. The initialization declaration: A := 50 or b := false. The types of a and B (int and bool) are inferred automatically by the compiler.
  2. This is the preferred form of using variables, but it can only be used inside functions, not declarations and assignments of global variables.
  3. In the same code block, we cannot use initialization declarations again for variables with the same name, but we can assign values;
  4. Declaring a local variable but not using it in the same code block also results in a compilation error
  5. Global variables can be declared but not used.
  6. _ is actually a write-only variable, and you can’t get its value. This is done because all declared variables must be used in Go, but sometimes you don’t need to use all the return values from a function.

3.2 constant

A constant is an identifier of a simple value, an amount that will not be modified while the program is running. The data types can only be Boolean, numeric (integer, floating point, and complex), and string. Constant definition format :(omits the type specifier [type] because the compiler can infer the type of a variable from its value.)

const identifier [type] = value
const b = "abc"
Copy the code

Multiple declarations of the same type can be abbreviated as:

const c_name1, c_name2 = value1, value2
Copy the code

Often used in enumeration:

Const (Unknown = 0 Female = 1 Male = 2) 0,1,2 represents Unknown, Female, and MaleCopy the code

Constants can be evaluated using len(), cap(), unsafe.sizeof () functions. In a constant expression, the function must be a built-in function, otherwise it will not compile.

Iota, special constants, can be considered as constants that can be modified by the compiler. Is reset to 0 when the const keyword is present (before the first line inside const). Iota counts once for each new line declared in const. The first ioTA is 0, and its value is automatically incremented each time ioTA is used on a new line.

package main

import "fmt"
const (
  i=1<<iota
  j=3<<iota
  k
  l
)

func main() {
  fmt.Println("i=",i)
  fmt.Println("j=",j)
  fmt.Println("k=",k)
  fmt.Println("l=",l)
}
Copy the code

I = 1, j= 6, k= 12, l= 24

Iota means automatically increse 1 from 0, so I =1<<0, j=3<<1 (<< means left shift), that is: I =1, j=6, this is no problem, the key is k and L, k=3<<2, L =3<<3.

To put it simply:

  • I =1: move 0 bit to the left and remain 1;
  • J =3: move 1 bit left, become binary 110, namely 6;
  • K =3: move 2 bits to the left and become binary 1100, i.e. 12;
  • L =3: shift 3 bits to the left and become binary 11000, i.e. 24.

Note: the < < n = = * (2 ^ n).

3.3 the enumeration

Enumeration, the value of a variable listed one by one, the variable is only listed within the range of values. There is no enumeration of this data type in Go, but it can be implemented using const in conjunction with the IOTA schema

3.3.1 Common Enumeration

const (
	a = 0
	b = 1
	c = 2
	d = 3
)
Copy the code

3.3.2 Enumeration

    1. Iota can only be used in constant expressions
    1. It starts at 0 by default and increments by 1 for each additional line in const with the same line value
const (
	a = iota //0
	c        //1
	d        //2
)
const (
	e, f = iota, iota //e=0, f=0
	g    = iota       //g=1
)
Copy the code
    1. If the IOTA is interrupted, it must be explicitly restored.
const (
  a = iota    //0
  b           //1
  c = 100     //100
  d           //100
  e = iota    //4
)
Copy the code

4 Go operator, control statement

4.1 the operator

Let’s say the value of A is 10 and the value of B is 20.

##1.1 Arithmetic operator

The operator describe The instance
+ add A + B Output 30
Subtracting the A-b The output is -10
* multiply The output of A * B is 200
/ division B / A 输出结果 2
% For more than B % A Displays 0
++ Since the increase A++ displays 11
Since the reduction of A– Outputs result 9

##1.2 Relational operators

The operator describe
= = Checks if two values are equal, returning True if they are, False otherwise.
! = Checks if two values are not equal, returning True if not False otherwise.
> Checks if the value on the left is greater than the value on the right, and returns True if so, False otherwise.
< Checks if the value on the left is less than the value on the right, and returns True if so, False otherwise.
> = Checks if the value on the left is greater than or equal to the value on the right, and returns True if so, False otherwise.
< = Checks if the value on the left is less than or equal to the value on the right, and returns True if it is False otherwise.

##1.3 Logical operators

The operator describe
&& The logical AND operator. Condition True if both operands are True, False otherwise.
| | The logical OR operator. Condition True if both operands have a True, otherwise False.
! Logical NOT operator. If the condition is True, the logic NOT condition False, otherwise True.

The ## 1.4-bit operator assumes that A is 60 and B is 13

The operator describe
& The bitwise and operator “&” is the binocular operator. Its function is to participate in the operation of two numbers corresponding to the binary phase and.
| The bitwise or operator “|” is a binary operator. Its function is to participate in the operation of two numbers corresponding to the binary phase or
^ The bitwise xor operator “^” is the binocular operator. Its function is that the corresponding binary digits of the two numbers involved in the operation are different or, when the corresponding binary digits are different, the result is 1.
<< The left-shift operator “<<” is the binocular operator. To the left n is 2 to the n. Its function is to “<<” left of all the binary operand left several bits, by “<<” right of the number to specify the number of moving, high discard, low fill 0.
>> The right-shift operator “>>” is the binocular operator. If you move to the right n places, you divide by 2 to the n. The function is to move all the binary digits of the operand to the left of “>>” several right, and the number to the right of “>>” specifies the number of digits to move.

The ##1.5 assignment operator

The operator describe The instance
= A simple assignment operator that assigns the value of an expression to an lvalue C = A + B Assigns the result of the A + B expression to C
+ = Add them and assign them C plus A is equal to C is equal to C plus A
– = Subtract and assign C minus A is equal to C is equal to C minus A
* = Multiply and assign C star is equal to A is equal to C times A
/ = Divide and assign C over A is equal to C is equal to C over A
% = Remainder and then assign C %= A is equal to C = C % A
< < = Assign after left shift C <<= 2 is equal to C = C << 2
> > = Assign after right shift C >>= 2 is equal to C = C >> 2
& = Assignment by bit and post C squared is equal to C squared is equal to C squared
^ = Xor post assignment by bit C to the 2 is equal to C is equal to C squared
= Assign C by bit or after

##1.6 Other operators

The operator describe The instance
& Returns the variable storage address &a; The actual address of the variable is given.
* Pointer to the variable *a; Is a pointer variable

# # 1.7 priority

priority The operator
5 * / % << >> & &^
4 + – | ^
3 = =! = < <= > >=
2 &&
1 ||

4.2 Control Statements

4.2.1 Conditional Statements

Specify one or more conditions and determine whether to execute the specified statement by testing whether the condition is true and executing additional statements if the condition is false.

4.2.1.1 the if statement

  • An if statement consists of a Boolean expression followed by one or more statements.
  • An if statement can be followed by an optional else statement, in which the expression is executed when the Boolean expression is false.
  • One or more if or else if statements can be embedded within an if or else if statement.
  • With all kinds of mainstream language, not to repeat. But notice that Go doesn’t have a ternary operator, so it’s not supported, right? : formal conditional judgment
package main

import "fmt"

func main(a) {
   /* Local variable definition */
   var a int = 100;
 
   /* Determine the Boolean expression */
   if a < 20 {
       /* If the condition is true, the following statement */ is executed
       fmt.Printf("A less than 20 \ n" );
   } else {
       /* If the condition is false, the following statement */ is executed
       fmt.Printf("A not less than 20\n" );
   }
   fmt.Printf("A value is: %d\n", a);

}
Copy the code

The execution result

A is not less than 20. The value of a is 100Copy the code

4.2.1.2 switch statement

  • For performing different actions based on different conditions, each case branch is unique and tested from top to bottom until a match is made.
  • Matches do not need to be followed by a break.
  • By default, a case has a break statement at the end of the case. After the match is successful, other cases will not be executed. If we need to execute subsequent cases, we can use Fallthrough.
  • Fallthrough: Enforces the following case statement. Fallthrough does not determine whether the result of the next case expression is true
switch x.(type){ case type: statement(s); case type: statement(s); Default: // Optional statement(s); }Copy the code

If the case has a fallthrough, the program will continue to execute the next case, and it will not determine whether the expression in the next case is true.

  1. Supports multi-condition matching
  2. There is no break separation between different cases, and only one case is executed by default
  3. If you want to execute multiple cases, use the fallthrough keyword or terminate with break
package main

import "fmt"

func main(a) {
   var x interface{}
     
   switch i := x.(type) {
      case nil:  
         fmt.Printf(" x 的类型 :%T",i)                
      case int:  
         fmt.Printf("X is int")                      
      case float64:
         fmt.Printf("X is float64")          
      case func(int) float64:
         fmt.Printf("X is of type func(int)")                      
      case bool.string:
         fmt.Printf("X is a bool or string" )      
      default:
         fmt.Printf("Unknown type")}}Copy the code

The execution result

Type of x :<nil>Copy the code
package main

import "fmt"

func main(a) {

    switch {
    case false:
            fmt.Println("1, case condition statement false")
            fallthrough
    case true:
            fmt.Println("2, case condition statement true")
            fallthrough
    case false:
            fmt.Println("3, case condition statement false")
            fallthrough
    case true:
            fmt.Println("4, case condition statement true")
    case false:
            fmt.Println("5, case condition statement false")
            fallthrough
    default:
            fmt.Println("6. Default case")}}Copy the code

The execution result

2,caseThe conditional statement istrue3,caseThe conditional statement isfalse4,caseThe conditional statement istrue
Copy the code

4.2.1.3 the select statement

select { case communication clause : statement(s); case communication clause : statement(s); Default: // Optional statement(s); }Copy the code
  • Each case must be a communication
  • All channel expressions are evaluated
  • All expressions that are sent are evaluated
  • If any communication can take place, it is executed and the others are ignored.
  • If multiple cases can be run, Select randomly and fairly selects one to execute. Others will not be executed. Otherwise:
    1. If there is a default clause, the statement is executed.
    2. If there is no default clause, select blocks until some communication can run; Go does not reevaluate channels or values.
package main

import "fmt"

func main(a) {

	ch1 := make(chan int.1)
	ch2 := make(chan int.1)
	The first one is closed, the second one is closed, the first one is closed, the second one is opened
	//ch1 <- 1
	//ch2 <- 1
	select {
	case <-ch1:
		fmt.Println("ch1 pop one element")
	case <-ch2:
		fmt.Println("ch2 pop one element")
	default:
		fmt.Println("not ready yet")}}Copy the code

The execution result

not ready yet
Copy the code

4.2.2 Loop Statements

4.2.2.1 the for loop

for init; condition; Post {} //for condition {} //while for {} init: specifies the initial value of a control variable. Condition: relational or logical expression, cyclic control condition; Post: Usually an assignment expression that increments or decays a control variable.Copy the code
package main

import "fmt"

func main(a) {
        sum := 0
        for i := 0; i <= 10; i++ {
                sum += i
        }
        fmt.Println(sum)
}
Copy the code

The execution result

55
Copy the code
package main

import "fmt"

func main(a) {
        sum := 1
        for ; sum <= 10; {
                sum += sum
        }
        fmt.Println(sum)

        // Write it like this, more like a While statement
        for sum <= 10{
                sum += sum
        }
        fmt.Println(sum)
}
Copy the code

The execution result

16
16
Copy the code

The range format of the for loop can iterate over slice, map, array, string, etc:

for key, value := range oldMap {
  newMap[key] = value
Copy the code
package main
import "fmt"

func main(a) {
        strings := []string{"google"."runoob"}
        for i, s := range strings {
                fmt.Println(i, s)
        }


        numbers := [6]int{1.2.3.5}
        for i,x:= range numbers {
                fmt.Printf("The value of bit %d x = %d\n", i,x)
        }  
}
Copy the code
0 Google 1 Runoob 0 x value = 1 1 x value = 2 2 x value = 3 3 x value = 5 4 x value = 0 5 x value = 0Copy the code

4.2.2.2 Loop Nesting

Loop loop, format:

for [condition | ( init; condition; increment ) | Range] { for [condition | ( init; condition;  increment ) | Range] { statement(s); } statement(s); }Copy the code
package main

import "fmt"

func main(a) {
   /* Define local variables */
   var i, j int

   for i=2; i < 100; i++ {
      for j=2; j <= (i/j); j++ {
         if(i%j==0) {
            break; // If a factor is found, it is not prime}}if(j > (i/j)) {
         fmt.Printf("%d is prime \n", i); }}}Copy the code

The execution result

2 is a prime 3 is a prime 5 is a prime 7 is a prime 11 is a prime 17 is a prime 19 is a prime 29 is a prime 31 is a prime 37 is a prime 41 is a prime 43 is a prime 47 is a prime 53 is a prime 59 is a prime 61 67 is a prime 71 is a prime 73 is a prime 79 is a prime 83 is a prime 89 is a prime 97 is a primeCopy the code

4.2.2.3 Loop Control Statements

    1. Break statement:
    • Used in a loop statement to break out of the loop and start executing the statement following the loop.
    • Break Breaks the function of a statement in switch after the execution of a case.
    • In multiple loops, you can label the loop you want to break with the label.
package main

import "fmt"

func main(a) {

    // Do not use tags
    fmt.Println("---- break ----")
    for i := 1; i <= 3; i++ {
        fmt.Printf("i: %d\n", i)
                for i2 := 11; i2 <= 13; i2++ {
                        fmt.Printf("i2: %d\n", i2)
                        break}}// Use the tag
    fmt.Println("---- break label ----")
    re:
        for i := 1; i <= 3; i++ {
            fmt.Printf("i: %d\n", i)
            for i2 := 11; i2 <= 13; i2++ {
                fmt.Printf("i2: %d\n", i2)
                break re
            }
        }
}
Copy the code

The execution result

---- break ----
i: 1
i2: 11
i: 2
i2: 11
i: 3
i2: 11
---- break label ----
i: 1
i2: 11    
Copy the code
    1. Continue statement: Skips the rest of the current loop and continues to the next loop.
package main

import "fmt"

func main(a) {
   /* Define local variables */
   var a int = 10

   /* for loop */
   for a < 20 {
      if a == 15 {
         /* Skip this loop */
         a = a + 1;
         continue;
      }
      fmt.Printf("A value is: %d\n", a); a++; }}Copy the code

The execution result

The value of a is: 10. The value of A is: 11. The value of A is: 12. The value of A is: 13. The value of A is: 14Copy the code
    1. Goto: unconditional transfer to the specified line in the process, with the conditional statement, to achieve conditional transfer, form a loop, out of the loop body, etc. (not recommended, cause confusion)
package main

import "fmt"

func main(a) {
   /* Define local variables */
   var a int = 10

   / * * / cycles
   LOOP: for a < 20 {
      if a == 15 {
         /* Skip the iteration */
         a = a + 1
         goto LOOP
      }
      fmt.Printf("A value is: %d\n", a)
      a++    
   }  
}
Copy the code

The execution result

The value of a is: 10. The value of A is: 11. The value of A is: 12. The value of A is: 13. The value of A is: 14Copy the code

5 Go Dictionary and character string

5.1 the dictionary

Map is a special data structure that can be found in any programming language. It is a key-value pair structure that can quickly obtain the corresponding value from a given key.

5.1.1 How do I define a Dictionary

package main

import "fmt"

var m1 map[string]int// The simplicity can be placed outside the function
func main(a) {
	m2 := make(map[int]interface{}, 100)// must be inside the function
	m3 := map[string]string{
		"name": "james"."age":  "35",}// must be inside the function
	fmt.Println(m2[1])
	fmt.Println(m3["name"])
	fmt.Println("Hello")}Copy the code

It is not necessary to specify the size of the dictionary when defining it, because the map can grow dynamically, but in cases where the map size can be predicted, it is best to indicate the size of the program in advance for efficiency purposes. It is important to note that dictionary keys cannot be compared with elements such as arrays, slices, etc. A value can be of any type. If you use interface{} as the value type, you can accept any type of value, but you need to use type assertions to determine the type.

5.1.2 Dictionary Operations

Putting elements into dictionaries is also very simple

package main

import "fmt"

func main(a) {
	m3 := map[string]string{
		"name": "james"."age":  "35",
	}
	fmt.Println(m3["name"])
	m3["key1"] = "v1"
	m3["key2"] = "v2"
	m3["key3"] = "v3"

	fmt.Println(m3["key2"])}Copy the code

You can try it out. What happens if you insert two elements with the same key? As with arrays and slicing, we can use len to get the length of the dictionary.

package main

import "fmt"

func main(a) {
	m3 := map[string]string{
		"name": "james"."age":  "35",
	}

	m3["key1"] = "v1"
	m3["key2"] = "v2"
	m3["key3"] = "v3"

	fmt.Println(len(m3))
}

Copy the code

In some cases, we can’t be sure whether the key-value pair exists or whether the current value is stored as an empty value. In GO, we can easily check this by using the following method.

package main

import "fmt"

func main(a) {
	m3 := map[string]string{
		"name": "james"."age":  "35",
	}

	value0, ok0 := m3["name"]

	fmt.Println(ok0)
	fmt.Println("v=" + value0)

	value1, ok1 := m3["n"]

	fmt.Println(ok1)
	fmt.Println("v=" + value1)

	value2 := m3["age"]
	fmt.Println("v=" + value2)
	// If supports one initialization statement. Initialization statements and criteria are separated by a semicolon
	if value, ok := m3["name"]; ok { // The condition is true, pointing to the {} statement
		fmt.Println("If contains the following: + value)
	}

}

Copy the code

This code returns true if there is a string with key name in the current dictionary, and false otherwise.

How do we iterate over an existing dictionary? This can be done as follows:

package main

import "fmt"

func main(a) {
	m3 := map[string]string{
		"name": "james"."age":  "35",}for key, value := range m3 {
		fmt.Println("key: ", key, " value: ", value)
	}

}

Copy the code

If we run the above program several times and find that the output order is different each time, the default for a dictionary is unordered, is there any way to make it ordered? You can give it a try. (Hint: You can do it by slicing.)

What if we want to delete a value that already exists in the dictionary and is no longer useful? This can be done using go’s built-in function delete.

package main

import "fmt"

func main(a) {
	m3 := map[string]string{
		"name": "james"."age":  "35"."key1": "--",}for key, value := range m3 {
		fmt.Println("key: ", key, " value: ", value)
	}
	delete(m3, "key1")

	_, ok := m3["key1"]
	fmt.Println(ok)
}
Copy the code

In addition to the simple operations above, we can also declare a dictionary of type slicing, a dictionary of type slicing, etc. You can try it out.

Not only that, we can store functions as value types in dictionaries.

package main

import "fmt"

func main(a) {
	m := make(map[string]func(a, b int) int)
	m["add"] = func(a, b int) int {
		return a + b
	}
	m["multi"] = func(a, b int) int {
		return a * b
	}
	m["devide"] = func(a, b int) int {
		return a / b
	}
	fmt.Println(m["add"] (3.2))
	fmt.Println(m["multi"] (3.2))
	fmt.Println(m["devide"] (3.2))}Copy the code

5.2 the string

Strings are one of the most common data types in any programming language, so let’s explore how strings are commonly manipulated in go. ##2.1 String definition A string is a value type whose value is immutable after it is created, meaning that the following operations are not allowed.

package main

func main() {
	s := "hello"
	s[0] = 'T'
}
Copy the code

The compiler prompts cannot assign to s[0]. In C, the end of a string is marked by \0, while in GO, the end of a string is marked by length.

If we want to modify the contents of a string, we can convert it to a byte slice and then to a string, but we also need to reallocate memory.

package main

import "fmt"

func main(a) {
	s := "hello"

	fmt.Println(s) //gello
	b := []byte(s)
	b[0] = 'g'
	s1 := ""
	s1 = string(b)
	fmt.Println(s1) //gello
}

Copy the code

As with any data type, the len function can be used to get the length of the string.

package main

import "fmt"

func main(a) {
	s := "hello"

	fmt.Println(len(s)) //gello

}
Copy the code

However, if the string contains Chinese characters, it cannot be directly manipulated by using byte slices, which is what we can do in go

package main

import (
	"fmt"
	"unicode/utf8"
)

func main(a) {
	s := "Hello China"
	fmt.Println(len(s))                    / / 17
	fmt.Println(utf8.RuneCountInString(s)) / / 9

	b := []byte(s)
	fmt.Println(len(b))
	for i := 0; i < len(b); i++ {
		fmt.Printf("%c", b[i])
	} / / helloa 1/2 level selections a 1/2 level a ¸ -a › 1/2 level
	fmt.Println()

	r := []rune(s)/ / and utf8 RuneCountInString with
	fmt.Println(len(r))
	for i := 0; i < len(r); i++ {
		fmt.Printf("%c", r[i])
	} // Hello China
}

Copy the code

In GO, strings are stored in UTF-8 encoding format, so each Chinese character is three bytes plus hello’s five bytes, so the length is 17. If we use utf8.runecountinString to get the length of the string containing Chinese characters, it fits our intuition. And since Chinese is not printable for each individual byte, you can see a lot of weird output, but converting strings to rune slices is fine.

5.2.2 strings package

The Strings package provides many functions to manipulate strings. Here you can see what functions are included golang.org/pkg/strings… .

Here are some examples:

package main

import (
	"fmt"
	"strings"
)

func main(a) {
	var str string = "This is an example of a string"
	// Checks whether the string starts with Th
	fmt.Printf("%t\n", strings.HasPrefix(str, "Th"))
	// Checks whether the string ends in aa
	fmt.Printf("%t\n", strings.HasSuffix(str, "aa"))
	// Determine if the string contains an substring
	fmt.Printf("%t\n", strings.Contains(str, "an"))}Copy the code

5.2.3 requires strconv package

The Strconv package implements conversion between basic data types and strings. Here you can see what functions are included golang.org/pkg/strconv… .

Here are some examples:

package main

import (
	"fmt"
	"strconv"
)

func main(a) {
	i, err := strconv.Atoi("- 42") // Convert a string to an int
	s := strconv.Itoa(- 42)        // Convert an int to a string
	fmt.Println(i, err, s)

}

Copy the code

If the conversion fails, return the corresponding error value.

5.2.4 String Concatenation

5.2.4.1 Sprintf

In addition to the above operations, string concatenation is also a very common operation. There are many ways to achieve string concatenation in GO language, but the efficiency of each method is not the same. The following compares these methods. (The content of the test will be explained in the later chapters, as long as you know these stitching methods can be)

1.Sprintf

//BenchmarkSprintf_test.go
package main

import (
	"fmt"
	"testing"
)

func BenchmarkSprintf(b *testing.B) {
	b.ResetTimer()
	for idx := 0; idx < b.N; idx++ {
		var s string
		for i := 0; i < numbers; i++ {
			s = fmt.Sprintf("%v%v", s, i)
		}
	}
	b.StopTimer()
}

Copy the code

5.2.4.2 + stitching

BenchmarkStringAdd_test.go

package main

import (
	"strconv"
	"testing"
)
const numbers = 100
func BenchmarkStringAdd(b *testing.B) {
	b.ResetTimer()
	for idx := 0; idx < b.N; idx++ {
		var s string
		for i := 0; i < numbers; i++ {
			s += strconv.Itoa(i)
		}
	}
	b.StopTimer()
}

Copy the code

5.2.4.3 bytes. The Buffer

BenchmarkBytesBuf_test.go

package main

import (
	"bytes"
	"strconv"
	"testing"
)
const numbers = 100
func BenchmarkBytesBuf(b *testing.B) {
	b.ResetTimer()
	for idx := 0; idx < b.N; idx++ {
		var buf bytes.Buffer
		for i := 0; i < numbers; i++ {
			buf.WriteString(strconv.Itoa(i))
		}
		_ = buf.String()
	}
	b.StopTimer()
}

Copy the code

5.2.4.4 strings. Builder joining together

BenchmarkStringBuilder_test.go

package main

import (
	"strconv"
	"strings"
	"testing"
)

const numbers = 100

func BenchmarkStringBuilder(b *testing.B) {
	b.ResetTimer()
	for idx := 0; idx < b.N; idx++ {
		var builder strings.Builder
		for i := 0; i < numbers; i++ {
			builder.WriteString(strconv.Itoa(i))
		}
		_ = builder.String()
	}
	b.StopTimer()
}

Copy the code

5.2.4.5 contrast

It is considered a benchmark and is executed by the “go test” command, plus the -bench flag. Multiple benchmarks are run in sequence.

$ go test-bench. goos: windows goarch: amd64 BenchmarkBytesBuf-12 1072903 1105 ns/op BenchmarkSprintf-12 71346 16485 ns/op BenchmarkStringAdd-12 209205 5777 Ns /op BenchmarkStringBuilder-12 1370254 879 ns/op PASS OK _/C_/Users/learn/go/GoHello 7.943sCopy the code

You can see the most efficient way to concatenate strings is through strings.Builder.

6 Go array and slice

6.1 an array

6.1.1 How do I Define an Array

An array is a contiguous group of data of the same type and fixed length. We can define arrays in the GO language in several ways.

/ / way
var arr1 = [5]int{}
2 / / way
var arr2 = [5]int{1.2.3.4.5}
3 / / way
var arr3 = [5]int{3:10}
Copy the code

Output the values of the above three variables as follows:

arr1 [0 0 0 0 0]
arr2 [1 2 3 4 5]
arr3 [0 0 0 10 0]
Copy the code
  • Method one does not specify an initial value for it when it is declared, so the values in the array are initialized to zero values of the type.
  • Method two defines initial values for the array in a display manner.
  • Method 3 assigns an initial value of 10 to the position with subscript 3 by subscript

[3]int and [4]int are two different data types.

6.1.2 How Do I Operate Data

We did not specify an initial value for arr1 above, so we can loop through the initial value later

for i := 0; i < len(arr1); i++ {
		arr1[i] = i * 10
	}
Copy the code

If we print the array arr1 again, we can see that the value has changed

arr1 update:  [0 10 20 30 40]
Copy the code

We can also loop through arrays of numbers. Unlike the above, the for loop provides such a way to facilitate arrays

for index, value := range arr1 {
		fmt.Printf("index: %d, value: %d\n", index, value)
	}
Copy the code

The first value is the index of the array and the second value is the corresponding value. The output is as follows:

index: 0, value: 0
index: 1, value: 10
index: 2, value: 20
index: 3, value: 30
index: 4, value: 40
Copy the code

If you don’t want an index, you can use _ instead.

6.1.3 Multidimensional Arrays

Go, like any other language, can define multidimensional arrays, optionally as follows:

var arr4 = [5] [5]int{{1.2.3.4.5},
		{6.7.8.9.10}},Copy the code

Output the current array, and you can see the values in the array as shown below

[[1 2 3 4 5] [6 7 8 9 10] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
Copy the code

6.1.4 Arrays as function arguments

When passing an array, go copies it, so passing a large array takes up a lot of memory. Therefore, it is rare to pass an array directly. To avoid this situation, we can use the following two methods:

  • Pass a pointer to an array
  • Pass slices (see the next section for details)

6.1.5 Pointer arrays and array Pointers

Arrays of Pointers and array Pointers are also often discussed in C or C ++, especially for beginners. It makes sense to put a “of” in the middle of each word, array of Pointers, array of Pointers.

1. Pointer arrays

In the case of pointer arrays, an array is filled with Pointers. Arrays are passed by value by default in GO, so if we change the array passed in a function, there is no effect on the original array.

func main(a) {
	var a [5]int
	fmt.Println(a)
	test(a)
	fmt.Println(a)
}

func test(a [5]int) {
	a[1] = 2
	fmt.Println(a)
}
Copy the code

The output

[0 0 0 0 0]
[0 2 0 0 0]
[0 0 0 0 0]
Copy the code

But what would be different if we had a function that passed an array of Pointers?

func main(a) {
	var a [5] *int
	fmt.Println(a)
	for i := 0; i < 5; i++ {
		temp := i
		a[i] = &temp
	}
	for i := 0; i < 5; i++ {
		fmt.Print("", *a[i])
	}
	fmt.Println()
	test1(a)
	for i := 0; i < 5; i++ {
		fmt.Print("", *a[i])
	}
}

func test1(a [5]*int) {
	*a[1] = 2
	for i := 0; i < 5; i++ {
		fmt.Print("", *a[i])
	}
	fmt.Println()
}
Copy the code

Let’s take a look at the results of the program

[<nil> <nil> <nil> <nil> <nil>]
 0 1 2 3 4
 0 2 2 3 4
 0 2 2 3 4
Copy the code

You can see that the initializers are all nil, so that validates that the pointer array is all pointer by pointer, and then we initialize it, and each pointer inside points to a block of memory.

Note: if a[I] = &i is initialized, then the array contains the same address, so the output must be the same

Then we pass the pointer array test1 function, the array parameters is still a copy of the pass is in the form of value, but because each element in the array is a pointer, so test1 function copies the values in the new array is still the pointer to the specific address values, then change a [1] the storage address points to the value of the Then the value of the original argument will also change to 2, as shown in the following figure.

2. Array Pointers

Now that we know about arrays of Pointers, let’s look at array Pointers, or Pointers to arrays, in go we can do something like this.

func main(a) {
	var a [5]int
	var aPtr *[5]int
	aPtr = &a
	// The short definition can also be aPtr := &a
	fmt.Println(aPtr)
	test(aPtr)
	fmt.Println(aPtr)
}

func test(aPtr *[5]int) {
	aPtr[1] = 5
	fmt.Println(aPtr)
}
Copy the code

We first define an array A, then aPtr, a pointer to array A, and pass the pointer to a function where we change the value and the output of the program

& [0 0 0 0 0] and [0 5 0 0 0] and [0 5 0 0 0]
Copy the code

We can see from the above figure that although the aPtr in main and test are different Pointers, they both point to the same memory space of the array, so operations on the array in either main or test directly change the array.

Section 6.2

Because arrays are of fixed length, they are not flexible enough in some situations, so GO provides a more convenient data type called slicing. Slice operation is similar to data, but its length is not fixed, you can add elements, if the current slice capacity will be automatically expanded. A slice can be defined in any of the following ways

package main

import "fmt"

func main(a) {
	// Declare an empty slice
	var s1 = []int{}
	// Declare a slice of length 3
	var s2 = []int{1.2.3}
	// Declare an empty slice of length 5
	var s3 = make([]int.5)
	// Declare a slice with length 5 and capacity 10
	var s4 = make([]int.5.10)

	// You can see that the definition of a slice is similar to that of an array, but you do not need to specify a length to define a slice.

	Len () and cap() can be used to obtain the length and capacity of the slice.
	fmt.Println("s1", s1, len(s1), cap(s1))
	fmt.Println("s2", s2, len(s2), cap(s2))
	fmt.Println("s3", s3, len(s3), cap(s3))
	fmt.Println("s4", s4, len(s4), cap(s4))

	// If we define a slice this way, it will be given the null value nil for the slice.
	var s5 []int
	fmt.Println("s5", s5, len(s5), cap(s5))

}
Copy the code

The execution result

s1 [] 0 0
s2 [1 2 3] 3 3
s3 [0 0 0 0 0] 5 5
s4 [0 0 0 0 0] 5 10
s5 [] 0 0
Copy the code

Slice operations We can continue to get slices from arrays and slices as follows

package main

import "fmt"

func main(a) {
	arr := [5]int{1.2.3.4.5}
	s := []int{6.7.8.9.10}

	s1 := arr[2:4] // Start bit 0, from the 2nd bit to the 4th bit
	s2 := arr[:3]  // Start from bit 0 to bit 3
	s3 := arr[2:]  // From the second digit to the final digit
	s4 := s[1:3]   // From first to third

	fmt.Println("s1:", s1)
	fmt.Println("s2:", s2)
	fmt.Println("s3:", s3)
	fmt.Println("s4:", s4)
}
Copy the code

The execution result

s1: [3 4]
s2: [1 2 3]
s3: [3 4 5]
s4: [7 8]
Copy the code

It can be seen that the slicing operation is “pack left but not right”. For example, arr[2:4] selects two elements with subscript 2,3 in arr array. If: there is no starting position on the left, it defaults to start at the beginning. Similarly, if there is no ending position on the right, it defaults to end.

6.2.2 Section expansion and splicing

We have already introduced the definition of slicing and some operations for slicing. Now let’s look at how to extend slicing.

package main

import "fmt"

func main(a) {
	a := []int{1.2.3}
	b := a[1:3]

	b = append(b, 4)
	b = append(b, 5)
	b = append(b, 6)
	b = append(b, 7)

	fmt.Println(a)
	fmt.Println(b)
}
Copy the code

The execution result

[1 2 3]
[2 3 4 5 6 7]
Copy the code

If you want to splice two slices, you can use the following method:

package main

import "fmt"

func main(a) {
	a := []int{1.2.3}
	b := a[1:3]

	fmt.Println(b)

	a = append(a, b...)// Two slices, key...

	fmt.Println(a)
}
Copy the code

The execution result

[1 2 3 2 3]Copy the code

The GO language also provides a very easy way to copy the values of one slice to another.

package main

import "fmt"

func main(a) {
	a := []int{1.2.3}
	b := make([]int.6)

	fmt.Println(a)
	fmt.Println(b)

	copy(b, a) //copy(copy)

	fmt.Println(a)
	fmt.Println(b)
}
Copy the code

The execution result

[1 2 3] [0 0 0 0 0 0] [1 2 3] [1 2 3 0 0 0 0]Copy the code

Try the results of the following operations:

What is the copy result of declaring that slice B is longer than slice A? When declaring slice B, its length is shorter than slice A, what is the replication result? When declaring a slice of b, its length is defined as 0. Will calling copy give an error?

package main

import "fmt"

func main(a) {
	a := []int{1.2.3}
	b := make([]int.6)
	c := make([]int.2)
	d := make([]int.0)

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)

	copy(b, a) // Copy is long
	copy(c, a) // Copy is short
	copy(d, a) //copy(copy) 0

	fmt.Println("a:", a)
	fmt.Println("b:", b)
	fmt.Println("c:", c)
	fmt.Println("d:", d)
}
Copy the code

The execution result

[1 2 3]
[0 0 0 0 0 0]
[0 0]
[]
a: [1 2 3]
b: [1 2 3 0 0 0]
c: [1 2]
d: []
Copy the code

Combine append with copy to do a lot of very useful things. You can also try 😀 to delete elements in index I insert elements in index I insert new slices of length j in index I

package main

import "fmt"

func main(a) {
	a := []int{1.2.3.4.5.6.7.8.9.0}
	i := 1
	// Delete the element in index I
	fmt.Println("Before deleting:", a)
	b := a[:i]
	c := a[i+1:]

	fmt.Println(append(b, c...) ) fmt.Println("After deletion:", a)

	// Insert the element at index I
	aa := []int{1.2.3.4.5.6.7.8.9.0}
	fmt.Println("Before inserting elements:", aa)
	d := aa[:i] / / 1
	e := aa[i:] / / 2,3,4,5,6,7,8,9,0

	ch := make([]int.len(d)+1) //
	copy(ch, d)
	ch[i] = 10
	f := append(ch, e...)
	fmt.Println("After inserting elements:", f)

	// Insert a new slice of length j at index I
	aaa := []int{1.2.3.4.5.6.7.8.9.0}
	ins := []int{11.22}
	fmt.Println("Before inserting elements:", aaa)
	dd := aaa[:i] / / 1
	ee := aaa[i:] / / 2,3,4,5,6,7,8,9,0

	chh := make([]int.len(dd))
	copy(chh, dd)
	chhh := append(chh, ins...)

	ff := append(chhh, ee...)
	fmt.Println("After inserting elements:", ff)
}
Copy the code

, etc.

Before insert: [1 2 3 4 5 6 7 8 9 0] [1 3 4 5 6 7 8 9 0] After insert: [1 3 4 5 6 7 8 9 0] Before insert: [1 2 3 4 5 6 7 8 9 0] After insert: [1 11 22 2 3 4 5 6 7 8 9 0] [1 2 3 4 5 6 7 8 9 0]Copy the code

6.2.3 Relationship between slice and array

For any slice, there is an underlying array corresponding to it. We can regard the slice as a window through which we can see some elements of the underlying array, as shown in the figure below.

7 Go function

7.1 Function Definition

In go, the function definition format is as follows:

func functionName([parameter list]) [returnTypes]{
    //body
}
Copy the code
  • Functions are declared by the func keyword.
  • FunctionName: indicates the functionName.
  • Parameter list: represents the list of parameters. The parameters of a function are optional and may contain or not contain parameters.
  • ReturnTypes: Return value types. Return values can be optional, with or without return values.
  • Body: To write the specific logic of the function

Example 1: The following function is used to find the sum of two numbers

package main

import "fmt"

func main(a) {
	result := GetSum(1.2)

	fmt.Println(Sum result 1:, result)

	result1 := GetSum1(1.2)

	fmt.Println(Sum result 2:, result1)
}

Num1, num2 */
func GetSum(num1 int, num2 int) int {
	result := num1 + num2
	return result
}

/** ** num1, num2 */
func GetSum1(num1, num2 int) int {
	result := num1 + num2
	return result
}
Copy the code

The execution result

The sum is 1:3. The sum is 2:3Copy the code

7.2 Value Passing and Reference Passing

Because of the existence of value and reference types in the GO language, this is also important when passing function parameters.

  • Value passing refers to the copying of arguments into a function during a function call so that changes to the arguments in the function do not affect the arguments.
  • Reference passing refers to passing the address of an argument to a function during a function call. Changes made to the argument in the function will affect the argument.

If we want the function to change the value of the argument directly, we can use pointer passing, passing the address of the variable as the argument to the function. Note: When defining function arguments — asterisk + argument variable type (e.g., a *int) — and size + variable (e.g., &a) example 2:

package main

import "fmt"

func paramFunc(a int, b *int, c []int) {
	a = 100
	*b = 100
	c[1] = 100

	fmt.Println("paramFunc:")
	fmt.Println(a, "The value before the change is 1.")
	fmt.Println(*b, "The value before the change is 1.")
	fmt.Println(c, "C [1] before the change is 2")}func main(a) {
	a := 1
	b := 1
	c := []int{1.2.3}
	paramFunc(a, &b, c)

	fmt.Println("main:")
	fmt.Println(a, "The value before the change is 1.")
	fmt.Println(b, "The value before the change is 1.")
	fmt.Println(c, "C [1] before the change is 2")}Copy the code

The execution result

ParamFunc: 100 The value before the change is 1. 100 The value before the change is 1 [1 100 3] C [1] The value before the change is 2. Main: 1 The value before the change is 1Copy the code

From the above output, we can see that A: value pass, B reference pass, C reference pass

7.3 Variable Length Parameters

Variable-length arguments are also supported in the GO language, but it is important to note that variable-length arguments must be placed at the end of function arguments, otherwise an error will be reported. The following code shows how to use variable length arguments like Example 3:

package main

import "fmt"

func main(a) {
	slice := []int{7.9.3.5.1}
	x := min(slice...) // Multiple parameters
	fmt.Printf("The minimum is: %d", x)

	y := min(4.5.3.2.8) // Multiple parameters
	fmt.Printf("The minimum is: %d", y)
}

func min(s ...int) int { / /... int
	if len(s) == 0 {
		return 0
	}
	fmt.Println()
	min := s[0]
	for i, v := range s { // I can be replaced with _ when not in use
		fmt.Println("loop ", i, v)
		if v < min {
			min = v
		}
	}
	return min
}
Copy the code

The execution result

loop  0 7
loop  1 9
loop  2 3
loop  3 5
loop  4 1
The minimum is: 1
loop  0 4
loop  1 5
loop  2 3
loop  3 2
loop  4 8
The minimum is: 2
Copy the code

7.4 Multiple Return Values

Another feature supported by the GO language is multiple return values. By returning the result and an error value, the function caller can easily know whether the function was successfully executed. This mode is also called command and OK mode, and it is recommended to use this mode in our future program design. The following code shows how to manipulate multiple return values. Example 4:

package main

import (
	"errors"
	"fmt"
)

func div(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("The divisor cannot be zero.")}return a / b, nil
}
func main(a) {
	result, err := div(1.2)
	iferr ! =nil {
		fmt.Printf("error: %v", err)
		return
	}
	fmt.Println("result:", result)

	// Another way to write this is as follows:
	if result, err := div(1.0); err ! =nil {
		fmt.Printf("error: %v", err)
		return
	} else {
		fmt.Println("result:", result)
	}
}
Copy the code

The execution result

Result: 0.5 error: The divisor cannot be zero.Copy the code

Note: Multiple return values need to be marked with ().

7.5 Naming return Values

In addition to the multiple return values supported above, it is also possible to name return values in GO. When a return is required, a simple return statement with no arguments is required. Let’s modify the division function above for example 5:

package main

import (
	"errors"
	"fmt"
)

func div(a, b float64) (result float64, err error) { Func div(a, b float64) (float64, error)
	if b == 0 {
		return 0, errors.New("The divisor cannot be zero.")
	}
	result = a / b
	return
}
func main(a) {

	// Another way to write this is as follows:
	if result, err := div(1.0); err ! =nil {
		fmt.Printf("error: %v", err)
		return
	} else {
		fmt.Println("result:", result)
	}
}
Copy the code

The execution result

error: The divisor cannot be zero.
Copy the code

Note: Even if there is only one named return value, you need to enclose it with ().

7.6 Anonymous Functions

An anonymous function is, as its name suggests, a function without a name, identical to a normal function except that it has no name. Anonymous functions can be called directly, saved to variables, as arguments, or as return values. Example 6:

package main

import (
	"fmt"
)

func main(a) {
	f := func(a) string {
		return "hello world"
	}
	fmt.Println(f())
}
Copy the code

The execution result

hello world
Copy the code

7.7 the closure

Closures can be interpreted as an encapsulation of a function with variables outside the function. Roughly speaking, a class contains variables and methods, and the closure contains external variables that correspond to static variables in the class. Why? Let’s look at an example first. Example 7:

package main

import (
	"fmt"
	"strconv"
)

func add(a) func(int) int {
	n := 10
	str := "string"
	return func(x int) int {
		n = n + x
		str += strconv.Itoa(x)
		fmt.Print(str, "")
		return n
	}
}
func main(a) {
	f := add()
	fmt.Println(f(1))
	fmt.Println(f(2))
	fmt.Println(f(3))

	f = add()
	fmt.Println(f(1))
	fmt.Println(f(2))
	fmt.Println(f(3))}Copy the code

The execution result

string1 11
string12 13
string123 16
string1 11
string12 13
string123 16
Copy the code

So this comes back to my original explanation. A closure is the encapsulation of a function and a variable outside the function that corresponds to a static variable in the class. This will make sense of the output of the program.

We start by declaring a function add, which returns an anonymous function inside the function body where n, STR and the following anonymous functions form the entire closure, and n and STR are like static variables in a class that are initialized only once, so even though we call the whole function multiple times, There were not initialized again And for operation is accumulation of external variables, which is consistent with the static variable in the class Notes in the language learning, the rain mark mentioned in the assembly code, closure returned not just anonymous functions, including the referenced environment variable pointer, it is similar with our previous explanation, Closures invoke corresponding variables by manipulating Pointers.

Quick question: Try out how to implement Fibonacci numbers using closures.

package main

import (
	"fmt"
)

func fib(a) func(int) int {
	return func(x int) int {
		if x < 0 {
			return - 1
		} else if x == 0 {
			return 0
		} else if x == 1 || x == 2 {
			return 1
		} else {
			f := fib()
			return f(x- 1) + f(x2 -)}}}func main(a) {
	f := fib()
	fmt.Print(f(0), "")
	fmt.Print(f(1), "")
	fmt.Print(f(2), "")
	fmt.Print(f(3), "")
	fmt.Print(f(4), "")
	fmt.Print(f(5), "")
	fmt.Print(f(6), "")
	fmt.Print(f(7), "")
	fmt.Print(f(8), "")
	fmt.Print(f(9), "")
	fmt.Print(f(10))}Copy the code

The execution result

0 1 1 2 3 5 8 13 21 34 55
Copy the code

8 Go structure, method, and interface

8.1 structure

The Go language has no concept of “classes,” nor does it support object-oriented concepts like inheritance. However, the structure and “class” of Go language are both composite structure, and the combination of structure in Go language has higher expansibility and flexibility than object-oriented.

8.1.1 Structure definition

Structures are generally defined as follows:

type identifier struct {
  field1 type1
  field2 type2
  ...
}
Copy the code

For example, we want to declare a student’s structure type:

type Student struct {
	Name string
	Age int
}
Copy the code

The type of a field in a structure can be any type, including function type, interface type, or even the structure type itself. For example, we declare the structure type of a node in a linked list.

type ListNode struct {
  Val int
  Next *ListNode
}
Copy the code

We can also declare structs without giving fields names, as shown in the following example

type Person struct{
	ID string
	int
}
Copy the code

We can see that one of the int fields has no name, which we call anonymous.

8.1.2 Operating structures

After declaring the structure, we need to create an instance of the structure. We can do this using the following methods, again using the Student structure above.

  • Using the new function creates a pointer to the structure type, automatically allocates memory to the structure, and assigns the corresponding zero value to each variable in the structure.
  • You can also declare structure types in the second way, noting that the order in which values are assigned to the structure is the same as the order in which the structure’s fields are declared.
  • The third method, which is more common, is to explicitly assign values to each field in the structure as we create it.

After you have declared the structure, you can directly manipulate the structure as follows.

package main

import "fmt"

type Student struct {
	Name string
	Age  int
	int
}

func main(a) {
	s1 := new(Student)              // The first way
	s2 := Student{"james".35.100} // The second way
	s3 := &Student{                 // The third way
		Name: "LeBron",
		Age:  36,}// Print the structure directly
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println(s3)

	// Directly manipulate the structure
	s1.Name = "jack"
	s1.Age = 35
	// Anonymous field
	s1.int = 85
	fmt.Println(s1)
}
Copy the code

The execution result

&{ 0 0}
{james 35 100}
&{LeBron 36 0}
&{jack 35 85}
Copy the code

We use s1.int directly to access anonymous fields in a structure and assign values to them. As we can see from this example, there can only be one anonymous field per data type for a structure.

8.1.3 label

In go, structs have an optional tag in addition to the field name and type. The tag is only accessible to reflect and is used for orM or JSON data passing. Alternatively, you can use the JSON package that comes with Go to convert declared structure variables into JSON strings. If we don’t label the structure, the output json string is:

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name string
	Age  int
	int
}
type Student1 struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	int  `json:score`
}

func ToJson(s *Student) (string, error) {
	bytes, err := json.Marshal(s)
	iferr ! =nil {
		return "".nil
	}
	return string(bytes), nil
}
func ToJson1(s *Student1) (string, error) {
	bytes, err := json.Marshal(s)
	iferr ! =nil {
		return "".nil
	}
	return string(bytes), nil
}
func main(a) {

	// If we don't label the structure
	s1 := new(Student)
	s1.Name = "jack"
	s1.Age = 28
	s1.int = 100

	str, _ := ToJson(s1)

	fmt.Println("If we don't label the structure.")
	fmt.Println(str)
	fmt.Println()

	s2 := new(Student1)
	s2.Name = "jack"
	s2.Age = 28
	s2.int = 100

	str1, _ := ToJson1(s2)
	fmt.Println("If we tag the structure.")
	fmt.Println(str1)
}
Copy the code

The execution result

If we don't label the structure {"Name":"jack"."Age":28} If we tag the structure {"name":"jack"."age"28} :Copy the code

8.1.4 Embedded structure

Before we introduced anonymous fields, structures can also be declared as anonymous fields as data types, so we call them inline structures. In this code, we embed structure A into structure B. We can easily manipulate the fields defined in A by inserting A structure into the variables of A structure B.

package main

import "fmt"

// Define A structure
type A struct {
	X, Y int
}

// define struct B, insert struct A
type B struct {
	A
	Name string
}

func main(a) {

	// Operate the struct with the struct embedded
	b := new(B)
	b.X = 10
	b.Y = 20
	b.Name = "jack"

	fmt.Println(b)
}
Copy the code

The execution result

&{{10 20} jack}
Copy the code

You can see that manipulating fields defined in structure A in B is just as natural as manipulating fields defined in structure B itself. But what if there is a field name conflict? If structure C also has field X, and embedded structure A also has field X, let’s try to see if it works.

package main

import "fmt"

// Define A structure
type A struct {
	X, Y int
}

// define struct B, insert struct A
type B struct {
	A
	Name string
}

// Define C struct with conflicting X field
type C struct {
	A
	B
	X int
}

func main(a) {

	// Struct with field conflicts
	c := new(C)
	c.X = 10 // Focus on fields
	c.Y = 20
	c.Name = "jack"

	fmt.Println(c)
	// I am only assigned to X under C, what if I am assigned to X under A and B?
	c.A.X = 11
	c.A.Y = 22
	c.B.X = 111
	c.B.Y = 222
	c.B.Name = "rose"

	fmt.Println(c)

}
Copy the code

The execution result

&{{0 20} {{0 0} jack} 10}
&{{11 22} {{111 222} rose} 10}
Copy the code

Note that inline structs are different from fields that declare a struct type. Try defining complex types such as slices in a struct

package main

import "fmt"

// Define A structure
type A struct {
	X, Y int
	Z    []int
}

func main(a) {
	v := new(A)
	v.X = 1
	v.Y = 2
	v.Z = make([]int.10)
	fmt.Println(v)

	v.Z[0] = 11
	v.Z[1] = 21
	v.Z[2] = 31
	v.Z[3] = 41
	v.Z[4] = 51
	fmt.Println(v)
}
Copy the code

The execution result

&{1 2 [0 0 0 0 0 0 0 0 0 0 0]} &{1 2 [11 21 31 41 51 0 0 0 0 0 0 0]}Copy the code

8.2 methods

8.2.1 Method Definition

Methods are similar to functions, except that they are defined with an argument between func and the method name, as follows:

func (r Receiver)func_name(a){
  //body
}
Copy the code

Where R is called the receiver of the method

package main

import "fmt"

type Person struct {
	name string
}

func (p Person) GetName(a) string {
	return p.name
}

func main(a) {
	p := Person{
		name: "james",
	}
	fmt.Println(p.GetName())
}
Copy the code

Where the receiver of GetName method is P is the Person structure type, that is, we bind a GetName method to the structure Person, we can use the above method to call.

8.2.2 Method recipients

There are two types of receivers for a method: value receivers and pointer receivers. The recipient of GetName above is the value receiver. We also define a pointer receiver for the Person structure.

The method defined by the value receiver is called with a copy of the value receiver, so any operation on the value does not affect the original type variable.

If you use a pointer receiver, however, changes in the body of the method will affect the original variable. Because the pointer also passes the address of the pointer itself, the copied pointer will still point to the original value, so the operation on the pointer receiver will also affect the value of the original variable.

package main

import "fmt"

type Person struct {
	name string
}

func (p Person) GetName(a) string {
	return p.name
}
func (p Person) SetName(name string) {
	p.name = name
}
func (p *Person) SetNameWithRef(name string) {
	p.name = name
}

func main(a) {
	p := Person{
		name: "james",
	}

	p.SetName("jack")

	fmt.Println("Pass the value to Jack", p.GetName())

	p.SetNameWithRef("jack")

	fmt.Println("Pointer passed to jack", p.GetName())
}
Copy the code

The execution result

The value is passed as Jack James and the pointer is passed as Jack jackCopy the code

There is also a special point in the GO language where we can use a pointer to call a method defined by the value receiver, and vice versa, as shown below:

package main

import "fmt"

type Person struct {
	name string
}

func (p Person) GetName(a) string {
	return p.name
}
func (p Person) SetName(name string) {
	p.name = name
}
func (p *Person) SetNameWithRef(name string) {
	p.name = name
}
func main(a) {
	p := &Person{
		name: "james",
	}
	p.SetName("kobe")
	fmt.Println(p.GetName())

	p1 := Person{
		name: "james",
	}
	p1.SetName("kobe")
	fmt.Println(p1.GetName())

	p2 := &Person{
		name: "james",
	}
	p2.SetNameWithRef("kobe")
	fmt.Println(p2.GetName())

	p3 := Person{
		name: "james",
	}
	p3.SetNameWithRef("kobe")
	fmt.Println(p3.GetName())
}
Copy the code

The execution result

james
james
kobe
kobe
Copy the code

8.3 interface

8.3.1 Interface Definition

An interface is a specification. It’s about who wants to implement what my interface does, not how. Interfaces in go are defined as follows:

type Namer interface {
    Method1(param_list) return_type
    Method2(param_list) return_type
    ...
}
Copy the code

8.3.2 Implementing the interface

There is no need to explicitly implement an interface in GO. As long as a type implements all the methods defined in the interface, the interface is implemented by default, and multiple types are allowed to implement the interface, and a single type is allowed to implement multiple interfaces.

package main

import "fmt"

// Define an animal interface
type Animal interface {
	Eat()
}

// Redefine the bird structure
type Bird struct {
	Name string
}

// Implement the eating interface
func (b Bird) Eat(a) {
	fmt.Println(b.Name + "Eat worms")}// Redefine the dog structure
type Dog struct {
	Name string
}

// Implement the eating interface
func (d Dog) Eat(a) {
	fmt.Println(d.Name + "Eat meat." ")}func EatWhat(a Animal) {
	a.Eat()
}
func main(a) {
	b := Bird{"The bird"}
	d := Dog{"Dog"}
	EatWhat(b)
	EatWhat(d)
}
Copy the code

The execution result

Birds eat worms and dogs eat meatCopy the code

In EatWhat we pass an Animal interface type. The Bird and Dog structures above implement the Animal interface, so they can be passed to the function to implement polymorphisms.

But there are a few things you need to explore:

  • Does the method defined by the value receiver and the pointer receiver have any impact on the implementation of the interface?
package main

import "fmt"

// Define an animal interface
type Animal interface {
	Eat()
}

// Redefine the bird structure
type Bird struct {
	Name string
}

// Implement the eating interface
func (b *Bird) Eat(a) {
	fmt.Println(b.Name + "Eat worms")}func EatWhat(a Animal) {
	a.Eat()
}
func main(a) {
	b := Bird{"The bird"}
	EatWhat(&b)
}
Copy the code

From the above results, the difference between value receivers and pointer receivers is the addition of the *& combination

  • Remember when we talked about embedded structures, if the embedded structure implements an interface what does that do to the external structure?
package main

import "fmt"

// Define an animal interface
type Animal interface {
	Eat()
}

// Redefine the bird structure
type Bird struct {
	Name string
}

// Implement the eating interface
func (b Bird) Eat(a) {
	fmt.Println(b.Name + "Eat worms")}// Redefine the dog structure
type Dog struct {
	Name string
	Bird
}

// Implement the eating interface
// func (d Dog) Eat() {
// fmt.Println(d.name + "eat meat ")
// }
func EatWhat(a Animal) {
	a.Eat()
}
func main(a) {
	b := Bird{"The bird"}
	d := &Dog{"Dog", b}
	EatWhat(b)
	EatWhat(d)
}
Copy the code

The execution result

Birds eat worms birds eat wormsCopy the code

Func (d Dog) Eat()

Birds eat worms and dogs eat meatCopy the code

8.3.3 Type Assertion

Sometimes the parameter passed by a method may be an interface type, but we need to determine which specific type to proceed with. This is where type assertion is used. Here is an example:

func IsDog(a Animal) bool {
	if v, ok := a.(Dog); ok {
		fmt.Println(v)
		return true
	}
	return false
}
Copy the code

The above method checks whether the passed argument is of type Dog and converts it to v if it is. Ok is used to indicate whether the assertion is successful.

However, if we have many subtypes to determine for a type, it is obviously complicated to write this way. We can use the following method:

func WhatType(a Animal) {
	switch a.(type) {
	case Dog:
		fmt.Println("Dog")
	case Bird:
		fmt.Println("Bird")
	default:
		fmt.Println("error")}}Copy the code

The whole piece of code

package main

import "fmt"

// Define an animal interface
type Animal interface {
	Eat()
}

// Redefine the bird structure
type Bird struct {
	Name string
}

// Implement the eating interface
func (b Bird) Eat(a) {
	fmt.Println(b.Name + "Eat worms")}// Redefine the dog structure
type Dog struct {
	Name string
}

// Implement the eating interface
func (d Dog) Eat(a) {
	fmt.Println(d.Name + "Eat meat." ")}func IsDog(a Animal) bool {
	if v, ok := a.(Dog); ok {
		fmt.Println(v)
		return true
	}
	return false
}
func WhatType(a Animal) {
	switch a.(type) {
	case Dog:
		fmt.Println("Dog")
	case Bird:
		fmt.Println("Bird")
	default:
		fmt.Println("error")}}func main(a) {
	b := Bird{"The bird"}
	d := Dog{"Dog"}
	fmt.Println(IsDog(b))
	fmt.Println(IsDog(d))
	WhatType(b)
	WhatType(d)
}
Copy the code

The execution result

false{} dogtrue
Bird
Dog
Copy the code

8.3.4 empty interface

An empty interface is a special type because there are no internal methods defined so it can represent any type. For example, the following operations can be performed:

package main

import "fmt"

func main(a) {
	var any interface{}

	any = 1
	fmt.Println(any)

	any = "hello"
	fmt.Println(any)

	any = false
	fmt.Println(any)
}
Copy the code

The execution result

1
hello
false
Copy the code

9 Go package management

9.1 What is Go Modules?

Go Modules encapsulates Modules and reuse code through package management. Here we only introduce the Go Modules management method. Go Modules was introduced in version 1.11 of Go and officially supported in version 1.12, which is an official package management solution provided by Go. Is a unit of source code exchange and versioning. The go command directly supports using Modules, including logging and resolving dependencies on other Modules

9.2 Use method of Go Modules

9.2.1 Environment Variables

You need to set environment variables first. You can run the go env command to view the current configuration.

$ go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\learn\AppData\Local\go-build
set GOENV=C:\Users\learn\AppData\Roaming\go\env  
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\learn\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\learn\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct      
set GOROOT=c:\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=c:\go\pkg\tool\windows_amd64       
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
Copy the code

If you need to change the GO111MODULE, use the go env command

$ go env -w GO111MODULE=on  
$ go env
set GO111MODULE=on
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\learn\AppData\Local\go-build
set GOENV=C:\Users\learn\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\learn\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\learn\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=c:\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=c:\go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=NUL
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\learn\AppData\Local\Temp\go-build821940786=/tmp/go-build -gno-record-gcc-switches
Copy the code

GO111MODULE

  • Auto: Enable go Modules as long as the project includes the go.mod file, which is still the default in Go1.11 through Go1.14.
  • On: Enable Go Modules. This is recommended and will be the default in future versions.
  • Off: Disables the Go Modules. This setting is not recommended.

GOPROXY This environment variable is used to design the Go Module proxy

GOSUMDB This environment variable is used to ensure consistency of module version data when pulling modules.

9.2.2 Initializing modules

You can create a new Module using the following command if you include the Go. Mod file in the directory:

$ go mod init test-demo
go: creating new go.mod: module test-demo
Copy the code

Go Modules automatically manage packages, so if you need to introduce dependencies, just add the following under go.mod

The module test - demo go 1.15 the require (github.com/astaxie/beego v1.12.0 github.com/shiena/ansicolor V0.0.0-20200904210342 - c7312218db18 / / indirect)Copy the code

Finally, the hello. go code looks like this:

  package main
  import "fmt"
  import "github.com/astaxie/beego"
  func main(a) {
      fmt.Println("hello")
      beego.Run()
  }
Copy the code

9.2.3 go get

The go get command is used to pull new dependencies. The following describes how to use the go get command

go get Pull dependencies, which pull the specified row (update) and do not update other modules on which it depends.
go get -u Updating an existing dependency forces updating all other modules on which it depends, excluding itself.
go get -u -t ./… Update all directly and indirectly dependent module versions, including those used in unit tests.

The other parameters

-d only downloads, not installs -f only works if you include the -u argument, does not allow -u to verify that each import has been fetched, This is especially useful for packages that are forked locally. -fix runs fix after getting the source code and then does anything else. -t also downloads packages that are needed to run testsCopy the code

9.2.4 Common Commands

Go mod init // Initialize go.mod go mod Tidy // Update the dependency file go mod download // Download the dependency file go mod vendor // Transfer the dependency to the local vendor file go mod Edit // Manually modify dependency file go mod graph // check existing dependency structure go mod verify //Copy the code

10 Handle the Go exception

10.1 the error

The Go language has a simple error interface built in as an error handling mechanism. The interface is defined as follows:

type error interface {
      Error() string
}
Copy the code

It contains an Error() method that returns string Go in two ways

10.1.1 first type: errors.new ()

package main

import (
	"errors"
	"fmt"
)

func main(a) {
	err := errors.New("This is an error")
	iferr ! =nil {
		fmt.Print(err)
	}
}
Copy the code

The execution result

API Server Listening at: 127.0.0.1:23464 This is an error Process Withdraw with code: 0Copy the code

10.1.2 Second type: fmt.errorf ()

package main

import (
	"fmt"
)

func main(a) {
	err := fmt.Errorf("This is an error")
	iferr ! =nil {
		fmt.Print(err)
	}
}
Copy the code

The execution result

API Server Listening at: 127.0.0.1:26641 This is an error Process Withdraw with code: 0Copy the code

In addition to using Go’s native methods directly, you can also customize errors. Here is an example of a function of natural numbers:

package main

import "fmt"

type NotNature float64

func (err NotNature) Error(a) string {
	return fmt.Sprintf("Numbers with natural numbers greater than or equal to 0: %v".float64(err))
}

/* * Natural number processing */
func Nature(x float64) (float64, error) {
	if x < 0 {
		return 0, NotNature(x)
	} else {
		return x, nil}}func main(a) {
	fmt.Println(Nature(1))
	fmt.Println(Nature(- 1))}Copy the code

The execution result

API Server Listening at: 127.0.1:9994 1 <nil> 0 A number of natural numbers greater than or equal to 0: -1 Process Withdraw with code: 0Copy the code

A few points to note:

    1. If a function needs to handle an exception, error is usually the last value returned by a multivalue, with an error value of nil indicating no exception and a non-nil indicating an exception.
    1. Error! Is usually handled first with an if statement. =nil. Normal logic goes after if.

In other words, it is not a predefined error in the run-time error scope. Certain behavior that does not meet expectations does not cause the program to fail to run (examples of natural number functions). Error should be used for exception handling. When the program has a major error, such as a few groups out of bounds, it is treated as a true exception and treated with Panic.

10.2 panic

Go does not use try… The catch method handles exceptions, using panic and Recover instead

package main

import (
	"errors"
	"fmt"
)

func main(a) {
	fmt.Println("Hello,Go!")
	panic(errors.New(" i am a error"))
	fmt.Println("hello,again!")}Copy the code

The execution result

API server listening at: 127.0.0.1:34729

Hello,Go!

panic: i am a error goroutine 1 [running]: main.main() c:/Users/learn/go/GoHello/main.go:10 +0x107

Process exiting with code: 0
Copy the code

As you can see from the above code, the programs behind Panic will not be executed. But we don’t catch exceptions to stop the program (as is usually the case), we catch exceptions to keep the program running, and that’s where RECOVER comes in.

package main

import "fmt"

func main(a) {
	defer func(a) {
		fmt.Println("I am the first printing function in defer")
		if err := recover(a); err ! =nil {
			fmt.Println(err)
		}
		fmt.Println("I am the second print function in defer")
	}()
	f()
}

func f(a) {
	fmt.Println("1")
	panic("I am panic")
	fmt.Println("2")}Copy the code

The execution result

API Server listening at: 127.0.0.1:47248 1 I was the first printing function in defer. I was panic. I was the second printing function in Defer -- Process Withdraw with code: 0Copy the code

As you can see, the f function starts printing normally, and when panic occurs, it jumps to the defer function and executes the contents of the defer function

Note that the err printed in the defer function is actually what was in Panic.

The following details the principles of Panic and Recover. First, take a look at the official definitions of Panic and Recover

func panic(v interface{})

The built-in function Panic stops normal execution of the current Goroutine. When the function F calls panic, the normal execution of F stops immediately. Any function delayed by F will run normally, and then F returns its caller. For caller G, a call to F is like a call to Panic, terminating G’s execution and running any delayed functions. Until execution of all functions in Goroutine stops in reverse order. At this point, the program terminates with a non-zero exit code. This stop sequence is known as panicking and can be controlled by the built-in function RECOVER.

func recover() interface{}

Recover built-in functions allow programs to manage the behavior of Panicking’s Goroutine. The recovery call is performed within the defer function (but not any function it calls), stopping the panicking sequence by resuming normal execution and retrieving the error values passed to the Panic call. If recover is called outside of the defer function, the sequence of Panicking does not stop. In that case, either when Goroutine is not panicking, or when the argument provided to Panic is nil, Recover returns nil. Therefore, recover’s return value reports whether Goroutine is panicking

Ps: defer and recover must be defined before panic, otherwise they are invalid.

10.3 Source Code Analysis

Errors. New is defined as follows

// src/errors/errors.go

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error(a) string {
	return e.s
}
Copy the code
    1. The New function returns an error formatted as the given text
    1. Even if the text is the same, each call to New returns a different error value.

11 Go reflex mechanism

11.1 What is reflection

The concept of reflection was first put forward by Smith in 1982, mainly as the ability of a program to access, detect and modify its own state or behavior.

The Go language provides a mechanism to update variables and check their values and call their methods at run time, but the exact types of these variables are not known at compile time. This is called reflection.

11.2 The role of reflection

11.2.1 When writing indeterminate parameter type functions, or when too many types are passed in

A typical application is object-relational mapping

package main

import (
	"database/sql"
	"time"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name         string
	Age          sql.NullInt64
	Birthday     *time.Time
	Email        string  `gorm:"type:varchar(100); unique_index"`
	Role         string  `gorm:"size:255"`        // set field size to 255
	MemberNumber *string `gorm:"unique; not null"` // set member number to unique and not null
	Num          int     `gorm:"AUTO_INCREMENT"`  // set num to auto incrementable
	Address      string  `gorm:"index:addr"`      // create index with name `addr` for address
	IgnoreMe     int     `gorm:"_"`               // ignore this field
}

func main(a) {
	var users []User
	db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
	iferr ! =nil {
		panic("failed to connect database")
	}
	db.Find(&users)
}

Copy the code

11.2.2 It is uncertain which function to call, and it needs to be executed dynamically according to certain conditions

func bridge(funcPtr interface{}, args ...interface{})
Copy the code

The first parameter funcPtr is passed in as an interface to the function pointer, and the function parameter args is passed in as a mutable parameter. The funcPtr function can be dynamically executed in the Bridge function using reflection.

11.3 Implementation of reflection

The reflection of Go is based on interface and type system, and the reflection mechanism of Go is carried out through interface. The Go language defines various types in the Reflect package and implements various functions for reflection that can detect type information and change type values at run time.

11.3.1 The Three laws of reflection

11.3.1.1 Reflection You can convert Interface Type Variable to Reflection Type Object.

Reflection provides a mechanism that allows a program to access data within an interface at run time. We’ll start with the reflect.Value and reflect.type methods in the Reflect.package

package main

import (
	"fmt"
	"reflect"
)

func main(a) {
	var Num float64 = 3.14

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

	fmt.Println("Reflect : Num.Value = ", v)
	fmt.Println("Reflect : Num.Type = ", t)
}
Copy the code

The execution result

Reflect : Num.Value =  3.14
Reflect : Num.Type  =  float64
Copy the code

The above example converts interface type variables to reflection type objects v and t, respectively, via reflect.ValueOf and reflect.TypeOf, where v is the ValueOf Num and t is the TypeOf Num. Take a look at the reflect.ValueOf and reflect.TypeOf function signatures

func TypeOf(i interface{}) Type
Copy the code
func (v Value) interface(a) (i interface{})
Copy the code

Both methods have parameter types that are null interfaces and throughout this process, when we call Reflect.typeof (x), when we call reflect.typeof (x), Num will be stored in this empty interface, and then reflect.typeof will disintegrate the empty interface, Converts interface type variables to reflection type variables

11.3.1.2 Reflection Can convert “Reflection Type Object” to “Interface Type Variable”

Law 2 is the reverse of law 1

package main

import (
	"fmt"
	"reflect"
)

func main(a) {
	var Num = 3.14
	v := reflect.ValueOf(Num)
	t := reflect.TypeOf(Num)
	fmt.Println(v)
	fmt.Println(t)

	origin := v.Interface().(float64)
	fmt.Println(origin)
}
Copy the code

The execution result

3.14
float64
3.14
Copy the code

11.3.1.3 To modify Reflection Type Object, its value must be writable

package main

import (
	"reflect"
)

func main(a) {
	var Num float64 = 3.14
	v := reflect.ValueOf(Num)
	v.SetFloat(6.18)}Copy the code

The execution result

panic: reflect: reflect.Value.SetFloat using unaddressable value goroutine 1 [running]: reflect.flag.mustBeAssignableSlow(0x8e) c:/go/src/reflect/value.go:260 +0x146 reflect.flag.mustBeAssignable(...) c:/go/src/reflect/value.go:247 reflect.Value.SetFloat(0xc7bb20, 0xc00009c000, 0x8e, 0x4018b851eb851eb8) c:/go/src/reflect/value.go:1619 +0x3e main.main() c:/Users/learn/go/GoHello/main.go:10 +0xba Process  exiting with code: 2 signal:false

Copy the code

Because reflection object V contains duplicate values, it cannot be modified. We can use the CanSet function to determine whether the reflection object can be modified as follows:

package main

import (
	"fmt"
	"reflect"
)

func main(a) {
	var Num float64 = 3.14
	v := reflect.ValueOf(Num)
	fmt.Println("Writability of V :", v.CanSet())
}
Copy the code

The execution result

Writability of v:false
Copy the code

11.4 Reflection practice

11.4.1 Modifying Content by Reflection

package main

import (
	"fmt"
	"reflect"
)

func main(a) {
	var f float64 = 3.41
	fmt.Println(f)
	p := reflect.ValueOf(&f)
	v := p.Elem()
	v.SetFloat(6.18)
	fmt.Println(f)

}
Copy the code

The execution result

3.41
6.18
Copy the code

11.4.2 Calling a method through Reflection

package main

import (
	"fmt"
	"reflect"
)

func hello(a) {
	fmt.Println("Hello world!")}func main(a) {
	hl := hello
	fv := reflect.ValueOf(hl)
	fv.Call(nil)}Copy the code

The execution result

Hello world!
Copy the code

Reflection makes code execute more slowly for a couple of reasons

  • 1 involves memory allocation and subsequent garbage collection
  • The 2 Reflect implementation has a lot of enumerations in it, which are for loops, such as types

Continue the Go language easy starter -2