Go language - Fundamentals of grammar

1, Go Foundation

1.1. init function

init function in go language is used for package initialization. This function is an important feature of go language.

It has the following characteristics:

   The init function is used to initialize the package before program execution, such as initializing variables in the package
 ​
      2 each package can have multiple init functions
 ​
      Each source file of package 3 can also have multiple init functions
 ​
      4 the execution order of multiple init functions in the same package is not clearly defined in go language (description)
 ​
      5. The init function of different packages determines the execution order of the initialization function according to the dependency of package import
 ​
      6 the init function cannot be called by other functions, but is automatically called before the main function is executed

1.2. main function

      Default entry function (main function) of Go language program: func main()
      The function body is wrapped in a pair of parentheses.
 ​
     func main(){
         // Function body
     }

1.3. Similarities and differences between init function and main function

  Similarities:
          The two functions cannot have any parameters and return values when they are defined, and the Go program calls them automatically.
      difference:
          init can be applied to any package, and multiple can be defined repeatedly.
          The main function can only be used in the main package, and only one can be defined.

Execution order of two functions

init() calls to the same go file are in top-down order.

For different files in the same package, the init() function in each file is called in the order of "from small to large" by comparing the file name string.

For different package, if it does not depend on each other, call the init() in its package according to the order of "first call of import" in the main package. If package exists, then the init() in the earliest package is called, and finally the main function is called.

If println() or print() is used in the init function, you will find that these two will not be executed in the order you think. These two functions are only recommended to be used in the test environment, but not in the formal environment.

1.4. Underline

'' is a special identifier used to ignore the result.

1.4.1. Underline in code

 package main
 ​
 import (
     "os"
 )
 ​
 func main() {
     buf := make([]byte, 1024)
     f, _ := os.Open("/Users/***/Desktop/text.txt")
     defer f.Close()
     for {
         n, _ := f.Read(buf)
         if n == 0 {
             break    
 ​
         }
         os.Stdout.Write(buf[:n])
     }
 }

Explanation:

    //1.
     Underline means to ignore this variable
 ​
      For example, os.Open, the return value is * os.File, error
 ​
      The common writing is f, err: = OS. Open ("XXXXXXX")
 ​
      If you don't need to know the returned error value at this time
 ​
      You can use F,:= os.Open("xxxxxx")
 ​
      In this case, the error variable is ignored
     
     //2.
       Placeholder means that the position should be assigned to a value, but we don't need this value.
      So give the value to the underline, which means don't throw it away.
      In this way, the compiler can optimize better, and single values of any type can be thrown into underscores.
      This is a placeholder. The method returns two results, and you only want one result.
      The other one uses "" to occupy the bit. If you use variables and don't use them, the compiler will report an error.

1.5. Variables and constants

1.5.1. Variable declaration

Variables in Go language can only be used after declaration. Repeated declaration is not supported in the same scope. And the variables of Go language must be used after declaration.

1.5.2. Standard statement

The variable declaration format of Go language is:

      var variable name variable type

The variable declaration starts with the keyword var, the variable type is placed after the variable, and there is no semicolon at the end of the line. for instance:

     var name string
     var age int
     var isOk bool

1.5.3. Batch declaration

     var (
         a string
         b int
         c bool
         d float32
     )

1.5.4. Initialization of variables

The default value for integer and floating-point variables is 0. The default value of a string variable is an empty string. Boolean variables default to false. The default of slice, function and pointer variables is nil.

      var variable name type = expression
     var name string = "Alex"
     var age int = 18
      // Initialize multiple variables at once
     var name, age = "brooks", 18

Declaration of short variables

     func main() {
         n := 10
         m := 100
     }

Declaration of anonymous variables

 func foo() (int, string) {
     return 10, "Q1mi"
 }
 func main() {
     x, _ := foo()
     _, y := foo()
     fmt.Println("x=", x)
     fmt.Println("y=", y)
 }
 // Anonymous variables do not occupy namespaces and do not allocate memory, so there are no duplicate declarations between anonymous variables. (in programming languages such as Lua, anonymous variables are also called dummy variables.)

be careful:

    Every statement outside a function must start with a keyword (var, const, func, etc.)
 ​
     := Cannot be used outside a function.
 ​
     _ Mostly used for placeholders, indicating that the value is ignored.

1.5.5. Constants

Declaration of constants

    const pi = 3.1415    // Constant must initialize assignment
   const e = 2.7182
     
   const (
      pi = 3.1415
      e = 2.7182
   )
     
   const (
      n1 = 100
      n2
      n3
  )

1.5.6. iota

    const (
             n1 = iota //0
             n2        //1
             n3        //2
             n4        //3
         )
 // Iota is a constant counter of go language, which can only be used in constant expressions. Iota will be reset to 0 when const keyword appears. Each new row constant declaration in const will make iota count once (iota can be understood as the row index in const statement block). Using iota simplifies the definition and is useful in defining enumerations.

1.6. Basic types

1.6.1. Integer

Integers are divided into the following two categories: int8, int16, int32 and int64. The corresponding unsigned integers are uint8, uint16, uint32 and uint64

Among them, uint8 is the well-known byte type, int16 corresponds to the short type in C language, and int64 corresponds to the long type in C language.

1.6.2. Floating point type

The Go language supports two floating-point numbers: float32 and float64.

1.6.3. Plural

complex64 and complex128

The complex number has real and imaginary parts. The real and imaginary parts of complex64 are 32 bits, and the real and imaginary parts of complex128 are 64 bits.

1.6.4. Boolean

    In Go language, Boolean data is declared as bool type. Boolean data has only two values: true (true) and false (false).
    
    The default value of Boolean variables is false.

    Cast integer to Boolean is not allowed in Go language

    Booleans cannot participate in numeric operations and cannot be converted to other types.

1.6.5. String

1.6.6. String escape

1.6.7. Common string operations

methodintroduce
len(str)Find length
+Or fmt.SprintfSplice string
strings.Splitdivision
strings.ContainsDetermine whether to include
strings.HasPrefix,strings.HasSuffixPrefix / suffix judgment
strings.Index(),strings.LastIndex()Where the substring appears
strings.Join(a[]string, sep string)join operation

1.7. Array

The Golang Array is very different from the previously recognized array.

    1. Array: a fixed length sequence of the same data type.
    2. Array definition: var a [len]int, such as var a [5]int. the array length must be constant and a component of the type. Once defined, the length cannot be changed.
    3. Length is part of the array type, so var a[5] int and var a[10]int are different types.
    4. The array can be accessed by subscript. The subscript starts from 0, and the subscript of the last element is len-1
    for i := 0; i < len(a); i++ {
    }
    for index, v := range a {
    }
    5. The access is out of bounds. If the subscript is outside the legal range of the array, the access is out of bounds and panic will be triggered
    6. Array is a value type. Assignment and transfer will copy the whole array, not the pointer. Therefore, changing the value of the copy will not change its own value.
    7. Support "= =" and "! =" operators, because memory is always initialized.
    8. Pointer array [n]*T, array pointer * [n]T.

1.7.1. Initialization of array

package main

import (
    "fmt"
)

var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}

func main() {
    a := [3]int{1, 2}           // Uninitialized element value is 0.
    b := [...]int{1, 2, 3, 4}   // The array length is determined by the initialization value.
    c := [5]int{2: 100, 4: 200} // Initialize the element with quotation marks.
    d := [...]struct {
        name string
        age  uint8
    }{
        {"user1", 10}, // Element types can be omitted.
        {"user2", 20}, // Don't forget the comma on the last line.
    }
    fmt.Println(arr0, arr1, arr2, str)
    fmt.Println(a, b, c, d)
}

Multidimensional array

package main

import (
    "fmt"
)

var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

func main() {
    a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // The second latitude cannot be "...".
    fmt.Println(arr0, arr1)
    fmt.Println(a, b)
}

Traversal of multidimensional array

 package main
 ​
 import (
     "fmt"
 )
 ​
 func main() {
 ​
     var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
 ​
     for k1, v1 := range f {
         for k2, v2 := range v1 {
             fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
         }
         fmt.Println()
     }
 }

1.8. Slice

Note that slice is not an array or array pointer. It refers to array fragments through internal pointers and related attributes to realize variable length scheme.

      1. Slice: slice is a reference of array, so slice is a reference type. But itself is a structure, and the value is copied and passed.
      2. The length of the slice can be changed, so the slice is a variable array.
      3. The slice traversal method is the same as that of array. You can use len() to find the length. Indicates the number of available elements. Read and write operations cannot exceed this limit. 
      4. cap can calculate the maximum expansion capacity of slice, which cannot exceed the array limit. 0 < = len (slice) < = len (array), where array is the array referenced by slice.
      5. Definition of slice: VAR variable name [] type, such as var STR [] string var arr [] int.
      6. If slice == nil, the results of len and cap are equal to 0.

1.8.1. Various ways to create slices

 
package main
 ​
 import "fmt"
 ​
 func main() {
    //1. Declaration slice
    var s1 []int
    if s1 == nil {
       fmt.Println("Is empty")
    } else {
       fmt.Println("Not empty")
    }
    // 2.:=
    s2 := []int{}
    // 3.make()
    var s3 []int = make([]int, 0)
    fmt.Println(s1, s2, s3)
    // 4. Initialization assignment
    var s4 []int = make([]int, 0, 0)
    fmt.Println(s4)
    s5 := []int{1, 2, 3}
    fmt.Println(s5)
    // 5. Slice from array
    arr := [5]int{1, 2, 3, 4, 5}
    var s6 []int
    // Front package and back package
    s6 = arr[1:4]
    fmt.Println(s6)
 }

1.8.2. Create slices through make

  var slice []type = make([]type, len)
    slice  := make([]type, len)
    slice  := make([]type, len, cap)

1.8.3. Operate slice with append built-in function (slice append)

package main

import (
    "fmt"
)

func main() {

    var a = []int{1, 2, 3}
    fmt.Printf("slice a : %v\n", a)
    var b = []int{4, 5, 6}
    fmt.Printf("slice b : %v\n", b)
    c := append(a, b...)
    fmt.Printf("slice c : %v\n", c)
    d := append(c, 7)
    fmt.Printf("slice d : %v\n", d)
    e := append(d, 8, 9, 10)
    fmt.Printf("slice e : %v\n", e)

}

1.8.4. slice traversal

package main

import (
    "fmt"
)

func main() {

    data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    slice := data[:]
    for index, value := range slice {
        fmt.Printf("inde : %v , value : %v\n", index, value)
    }

}

Understanding of slice colon

golang slice data[:6:8] understanding of two colons

Conventional slice, data [6:8], from the 6th bit to the 8th bit (returns 6, 7), the length len is 2, and the maximum expandable length cap is 4 (6-9)

Another writing method: data[:6:8] each number is preceded by a colon. The slice content is data from 0 to bit 6, the length len is 6, and the maximum extension cap is set to 8

a[x:y:z] slice content [x:y] slice length: y-x slice capacity: z-x

1.9. Pointer

You only need to know three concepts: pointer address, pointer type and pointer value

1.9.1. Pointer in go language

Function parameters in Go language are value copies. When we want to modify a variable, we can create a pointer variable pointing to the address of the variable. Passing data uses pointers instead of copying data. Type pointers cannot be offset and evaluated. The pointer operation in Go language is very simple. You only need to remember two symbols: & (take the address) and * (take the value according to the address).

1.9.2. Pointer address and pointer type

Each variable has an address at runtime, which represents the location of the variable in memory. In Go language, the & character is placed in front of the variable to "take the address" the variable. Value types (int, float, bool, string, array, struct) in Go language have corresponding pointer types, such as: * int, * int64, * string, etc.

The syntax of getting variable pointer is as follows:

    PTR: = & V / / the type of V is T

Of which:

    v: The variable representing the address to be fetched. The type is T
    ptr: the variable used to receive the address. The type of ptr is * t, which is called the pointer type of T* Represents a pointer.

for instance:

func main() {
    a := 10
    b := &a
    fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
    fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
    fmt.Println(&b)                    // 0xc00000e018
}

1.9.3. Pointer value

func main() {
    //Pointer value
    a := 10
    b: = & A / / take the address of variable a and save the pointer to b
    fmt.Printf("type of b:%T\n", b)
    C: = * B / / value of pointer (value of memory according to pointer)
    fmt.Printf("type of c:%T\n", c)
    fmt.Printf("value of c:%v\n", c)
}

//Summary: the address taking operator & and the value taking operator * are a pair of complementary operators, & take out the address, * take out the value pointed to by the address according to the address.

Example of pointer passing value:

func modify1(x int) {
    x = 100
}

func modify2(x *int) {
    *x = 100
}

func main() {
    a := 10
    modify1(a)
    fmt.Println(a) // 10
    modify2(&a)
    fmt.Println(a) // 100
}

1.9.4. Null pointer

  • When a pointer is defined and not assigned to any variable, its value is nil

  • Null pointer judgment

 package main
 ​
 import "fmt"
 ​
 func main() {
     var p *string
     fmt.Println(p)
     fmt.Printf("p The value of is%v\n", p)
     if p != nil {
         fmt.Println("Non empty")
     } else {
         fmt.Println("Null value")
     }
 }

1.9.5. Difference between new and make

      1. Both are used for memory allocation.
      2.make is only used for the initialization of slice, map and channel, and returns the three reference types themselves;
      3. new is used for memory allocation of type, and the value corresponding to memory is the type zero value, and the returned pointer is to the type.

1.10. Map

map is an unordered key value based data structure. map in Go language is a reference type and must be initialized before it can be used.

1.10.1. Definition of map

The definition syntax of map in Go language is as follows

     map[KeyType]ValueType

Among them,

      KeyType: indicates the type of key.
 ​
      ValueType: indicates the type of value corresponding to the key.

The default initial value of a map type variable is nil. You need to use the make() function to allocate memory. The syntax is:

    make(map[KeyType]ValueType, [cap])

cap represents the capacity of the map. Although this parameter is not necessary, we should specify an appropriate capacity for the map when initializing it

1.10.2. Basic use of map

The data in the map appears in pairs. The basic usage example code of the map is as follows:

func main() {
    scoreMap := make(map[string]int, 8)
    scoreMap ["Zhang San"] = 90
    scoreMap ["Xiaoming"] = 100
    fmt.Println(scoreMap)
    fmt.Println(scoreMap ["Xiaoming"])
    fmt.Printf("type of a:%T\n", scoreMap)
}

//Output
	/*map [Xiao Ming: 100 sheets three: 90]
    100
    type of a:map[string]int*/

1.10.3. Judge whether a key exists

 value, ok := map[key]
func main() {
    scoreMap := make(map[string]int)
    scoreMap ["Zhang San"] = 90
    scoreMap ["Xiaoming"] = 100
    //If the key exists, ok is true and V is the corresponding value; There is no zero value with ok as false and V as value type
    v. OK: = scoremap ["Zhang San"]
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("no such person found")
    }
}

1.10.4. Traversal of map

Go language uses for range to traverse map.

func main() {
    scoreMap := make(map[string]int)
    scoreMap ["Zhang San"] = 90
    scoreMap ["Xiaoming"] = 100
    scoreMap ["king five"] = 60
    for k, v := range scoreMap {
        fmt.Println(k, v)
    }
}

But when we just want to traverse the key, we can write it as follows:

func main() {
    scoreMap := make(map[string]int)
    scoreMap ["Zhang San"] = 90
    scoreMap ["Xiaoming"] = 100
    scoreMap ["king five"] = 60
    for k := range scoreMap {
        fmt.Println(k)
    }
}

Note: the order of elements when traversing the map is independent of the order in which key value pairs are added.

1.11. Structure

There is no concept of "class" in go language, nor does it support object-oriented concepts such as inheritance of "class". Go language has higher expansibility and flexibility than object-oriented through the embedding of structure and matching interface.

1.11.1. Type alias and user-defined type

Custom type

There are some basic data types in Go language, such as string, integer, floating point, Boolean and other data types. In Go language, you can use the type keyword to define user-defined types.

   //Define MyInt as type int
    type MyInt int
   //Through the definition of Type keyword, MyInt is a new Type, which has the characteristic of int.

Type alias

Type aliasing is a new feature added in Go1.9.

Type alias specifies that TypeAlias is only the alias of type, which is essentially the same type as type. Just like a child who had a nickname and a nickname when he was a child and used a scientific name after school, the English teacher would give him an English name, but these names all refer to him.

    type TypeAlias = Type

rune and byte we have seen before are type aliases. Their definitions are as follows:

    type byte = uint8
    type rune = int32

1.11.2. Difference between type definition and type alias

//Type definition
type NewInt int

//Type alias
type MyInt = int

func main() {
    var a NewInt
    var b MyInt

    fmt.Printf("type of a:%T\n", a) //type of a:main.NewInt
    fmt.Printf("type of b:%T\n", b) //type of b:int
}
/*
The result shows that the type of a is main.NewInt, which indicates the NewInt type defined under the main package. The type of b is int. MyInt type only exists in the code, and there will be no MyInt type when compilation is completed.
*/

1.11.3. Definition of structure

Use the type and struct keywords to define the structure. The specific code format is as follows:

    Type type name struct{
        Field name field type
        Field name field type
        ...
    }

Of which:

    1. Type name: identifies the name of a user-defined structure. It cannot be repeated in the same package.
    2. Field name: indicates the field name of the structure. Field names in the structure must be unique.
    3. Field type: indicates the specific type of structure field.

For example, we define a Person structure with the following code:

     type person struct {
         name string
         city string
         age  int8
     }

Fields of the same type can also be written on one line,

     type person1 struct {
         name, city string
         age        int8
     }

1.11.4. Basic instantiation

 type person struct {
     name string
     city string
     age  int8
 }
 ​
 func main() {
     var p1 person
     p1.name = "pprof.cn"
      p1.city = "Beijing"
     p1.age = 18
       fmt.Printf("p1=%v\n", p1)   // p1={pprof.cn Beijing 18}
      fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"pprof.cn", city: "Beijing", age:18}
 }
 // We use. To access the fields (member variables) of the structure, such as p1.name and p1.age.

1.11.5. Anonymous structure

 package main
 ​
 import (
     "fmt"
 )
 ​
 func main() {
     var user struct{Name string; Age int}
     user.Name = "pprof.cn"
     user.Age = 18
     fmt.Printf("%#v\n", user)
 }

1.11.6. Create pointer type structure

We can also instantiate the structure by using the new keyword to get the address of the structure. The format is as follows:

     var p2 = new(person)
     fmt.Printf("%T\n", p2)     //*main.person
     fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}

From the printed results, we can see that p2 is a structure pointer.

It should be noted that the Go language supports the direct use of structure pointers to access the members of structures.

     var p2 = new(person)
      p2.name = "test"
     p2.age = 18
       p2.city = "Beijing"
      FMT. Printf ("P2 =%#v \ n", P2) / / P2 = & main. Person {Name: "test", city: "Beijing", age:18}

1.11.7. Take the address of the structure for instantiation

Using & to get the address of a structure is equivalent to a new instantiation of the structure type.

p3 := &person{}
fmt.Printf("%T\n", p3)     //*main.person
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"", city:"", age:0}
p3.name = "blog"
p3.age = 30
 p3.city = "Chengdu"
FMT. Printf ("P3 =%#v \ n", P3) / / P3 = & main. Person {Name: "blog", city: "Chengdu", age:30}

p3.name = "blog" is actually (* p3).name = "blog" at the bottom, which is a syntax sugar implemented by Go language.

1.11.8. Structure initialization

type person struct {
    name string
    city string
    age  int8
}

func main() {
    var p4 person
    fmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"", city:"", age:0}
}

1.11.9. Initialization using key value pairs

When initializing a structure with a key value pair, the key corresponds to the field of the structure, and the value corresponds to the initial value of the field.

p5 := person{
    name: "pprof.cn",
    city: "Beijing",
    age:  18,
}
fmt.Printf("p5=%#v\n", p5) //p5=main.person{name:"pprof.cn", city: "Beijing", age:18}

You can also initialize the structure pointer with key value pairs, for example:

p6 := &person{
    name: "pprof.cn",
    city: "Beijing",
    age:  18,
}
FMT. Printf ("P6 =%#v \ n", P6) / / P6 = & main. Person {Name: "pprof. CN", city: "Beijing", age:18}

When some fields have no initial value, the field can not be written. At this time, the value of the field without an initial value is the zero value of the field type.

p7 := &person{
    city: "Beijing",
}
FMT. Printf ("P7 =%#v \ n", P7) / / P7 = & main. Person {Name: "", city: "" Beijing ", age:0}

1.11.10. Structure memory layout

type test struct {
    a int8
    b int8
    c int8
    d int8
}
n := test{
    1, 2, 3, 4,
}
fmt.Printf("n.a %p\n", &n.a)
fmt.Printf("n.b %p\n", &n.b)
fmt.Printf("n.c %p\n", &n.c)
fmt.Printf("n.d %p\n", &n.d)
/*Output results
	n.a 0xc0000a0060
    n.b 0xc0000a0061
    n.c 0xc0000a0062
    n.d 0xc0000a0063
*/

1.11.11. Constructor

The structure of Go language does not have a constructor, so we can implement it ourselves. For example, the following code implements a person constructor. Because struct is a value type, if the structure is complex, the performance cost of value copy will be large, so the constructor returns the structure pointer type.

func newPerson(name, city string, age int8) *person {
    return &person{
        name: name,
        city: city,
        age:  age,
    }
}

Call constructor

P9: = newperson ("pprof. CN", "test", 90)
fmt.Printf("%#v\n", p9)

2, Process control

2.1. Conditional statement if

2.1.1. Basic grammar

The syntax of if statement in Go programming language is as follows:

    • conditional expression parentheses can be omitted.
    • with initialization statements, you can define code block local variables. 
    • the left parenthesis of the code block must be at the end of the conditional expression.

    if Boolean expression{
    /*Execute when Boolean expression is true*/
    }

2.2. Conditional statement switch

2.2.1. Type Swich

The switch statement can also be used for type switch to determine the type of variable actually stored in an interface variable.

switch x.(type){
    case type:
       statement(s)      
    case type:
       statement(s)
    /*You can define any number of case s*/
    default: / * optional*/
       statement(s)
}

example:

package main

import "fmt"

func main() {
    var x interface{}
    //Writing method 1:
    switch i := x.(type) { // With initialization statement
    case nil:
        fmt.Printf(" x Type of :%T\r\n", i)
    case int:
        fmt.Printf("x yes int type")
    case float64:
        fmt.Printf("x yes float64 type")
    case func(int) float64:
        fmt.Printf("x yes func(int) type")
    case bool, string:
        fmt.Printf("x yes bool or string type")
    default:
        fmt.Printf("Unknown type")
    }
    //Writing method 2
    var j = 0
    switch j {
    case 0:
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("def")
    }
    //Writing method III
    var k = 0
    switch k {
    case 0:
        println("fallthrough")
        fallthrough
        /*
            Go The switch is very flexible. The expression does not have to be a constant or integer. The process is performed from top to bottom until a match is found;
            If switch has no expression, it will match true.
            Go The default switch is equivalent to a break at the end of each case,
            After successful matching, other case s will not be automatically executed downward, but the whole switch will jump out,
            However, you can use fallthrough to enforce the following case code.
        */
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("def")
    }
    //Writing method III
    var m = 0
    switch m {
    case 0, 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("def")
    }
    //Writing method 4
    var n = 0
    switch { //Omit the conditional expression, which can be used as if...else if...else
    case n > 0 && n < 10:
        fmt.Println("i > 0 and i < 10")
    case n > 10 && n < 20:
        fmt.Println("i > 10 and i < 20")
    default:
        fmt.Println("def")
    }
}

2.3. Conditional statement select

2.3.1. select statement

The select statement is similar to the switch statement, but select randomly executes a runnable case. If there is no case to run, it will block until there is a case to run.

select is a control structure in Go, similar to the switch statement used for communication. Each case must be a communication operation, either sending or receiving. select randomly executes a runnable case. If there is no case to run, it will block until there is a case to run. A default clause should always be runnable.

 select {
     case communication clause  :
        statement(s);      
     case communication clause  :
        statement(s);
     /*  You can define any number of case s*/
      default: / * optional*/
        statement(s);
 }
 ​
 /*
      Each case must be a communication
       All channel expressions are evaluated
       All expressions sent are evaluated
       If any one communication is available, it executes; others are ignored.
      If multiple case s can be run, Select will randomly and fairly Select one to execute. Others will not execute.
      Otherwise:
      If there is a default clause, the statement is executed.
      If there is no default sentence, select will block until a communication can run; Go will not re evaluate the channel or value.
 */

2.4. Circular statement for

Syntax:

     for init; condition; post { }
     for condition { }
     for { }
      init: it is generally an assignment expression, which assigns an initial value to the control variable;
      Condition: relational expression or logical expression, loop control condition;
      post: it is generally an assignment expression, which increments or decrements the control variable.
      The execution process of the for statement is as follows:
      ① First, the expression init is given an initial value;
      ② Judge whether the assignment expression init meets the given condition. If its value is true and meets the loop condition, execute the statement in the loop, then execute post, enter the second loop, and then judge the condition; otherwise, judge that the value of the condition is false and do not meet the condition, terminate the for loop and execute the statement outside the loop.

example:

 package main
 ​
 import "fmt"
 ​
 func main() {
 ​
    var b int = 15
    var a int
 ​
    numbers := [6]int{1, 2, 3, 5}
 ​
    /* for loop */
    for a := 0; a < 10; a++ {
       fmt.Printf("a The value of is: %d\n", a)
    }
 ​
    for a < b {
       a++
       fmt.Printf("a The value of is: %d\n", a)
       }
 ​
    for i,x:= range numbers {
       fmt.Printf("The first %d position x Value of = %d\n", i,x)
    }   
 }

2.5. Circular statement range

Golang range is similar to iterator operation and returns (index, value) or (key, value).

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

for key, value := range oldMap {
    newMap[key] = value
}
1st value2nd value
stringindexs[index]unicode, rune
array/sliceindexs[index]
mapkeym[key]
channelelement

You can ignore the unwanted return value or the special variable "_".

package main

func main() {
    s := "abc"
    // Ignore 2nd value and support string/array/slice/map.
    for i := range s {
        println(s[i])
    }
    // Ignore index.
    for _, c := range s {
        println(c)
    }
    // Ignore all return values and iterate only.
    for range s {

    }

    m := map[string]int{"a": 1, "b": 2}
    // Returns (key, value).
    for k, v := range m {
        println(k, v)
    }
}

*Note that range copies the object.

The other two reference types map and channel are pointer wrappers, unlike slice, which is struct.

What's the difference between for and for range?

Mainly due to different usage scenarios

for can

Traversing array and slice

Traverse the map whose key is an integer increment

Traversal string

for range can do all the things that for can do, but can do what for can't do, including

Traverse the map with the key of string type and get the key and value at the same time

Traverse channel

2.6. Cycle control Goto, Break, Continue

Loop control statement

Loop control statements can control the execution of statements in a loop.

GO language supports the following loop control statements:

2.6.1. Goto,Break,Continue

     1. All three statements can be used with a label
    2. The tag name is case sensitive. If it is not used in the future, it will cause compilation errors
    3.continue and break labels can be used to jump out of multi-layer loops
    4.goto is to adjust the execution position, which is different from the results of continue and break labels

3, Functions

3.1. Definition of function

Features of golang function:

	• no need to declare prototypes.
    • support variable parameters.
    • support multiple return values.
    • support named return parameters. 
    • support anonymous functions and closures.
    • a function is also a type, and a function can be assigned to a variable.

    • nested is not supported. A package cannot have two functions with the same name.
    • overload is not supported 
    • default parameter is not supported.

3.1.1. Function declaration

The function declaration contains a function name, parameter list, return value list and function body. If the function does not return a value, the return list can be omitted. The function is executed from the first statement until the return statement or the last statement of the function is executed.

Functions can have no arguments or accept multiple arguments.

Note that the type is after the variable name.

When two or more consecutive function named parameters are of the same type, all but the last type can be omitted.

Function can return any number of return values.

Use the keyword func to define a function, and the left brace still cannot start another line.

func test(x, y int, s string) (int, string) {
    //Adjacent parameters of the same type can be merged. Multiple return values must be in parentheses.
    n := x + y          
    return n, fmt.Sprintf(s, n)
}

Functions are the first type of objects that can be passed as parameters. It is recommended to define complex signatures as function types for easy reading.

 package main
 ​
 import "fmt"
 ​
 func test(fn func() int) int {
     return fn()
 }
 // Define the function type.
 type FormatFunc func(s string, x, y int) string 
 ​
 func format(fn FormatFunc, s string, x, y int) string {
     return fn(s, x, y)
 }
 ​
 func main() {
     s1 := test(func() int { return 100 }) // Use anonymous functions as arguments directly.
 ​
     s2 := format(func(s string, x, y int) string {
         return fmt.Sprintf(s, x, y)
     }, "%d, %d", 10, 20)
 ​
     println(s1, s2)
 }

3.2. Parameters

    func myfunc(args ...int) {     // 0 or more parameters
   }
 ​
    func add(a int, args…int) int {     // 1 or more parameters
   }
 ​
    func add(a int, b int, args…int) int {     // 2 or more parameters
   }
   // Note: args is a slice. We can access all parameters in turn through arg[index], and judge the number of passed parameters through len(arg)

Passing arbitrary type data with interface {} is the customary usage of Go language, and interface {} is type safe.

 func myfunc(args ...interface{}) {
   }

code:

 package main
 ​
 import (
     "fmt"
 )
 ​
 func test(s string, n ...int) string {
     var x int
     for _, i := range n {
         x += i
     }
 ​
     return fmt.Sprintf(s, x)
 }
 ​
 func main() {
     println(test("sum: %d", 1, 2, 3))
 }

When using slice object as variable parameter, it must be expanded. (slice...)

package main

import (
    "fmt"
)

func test(s string, n ...int) string {
    var x int
    for _, i := range n {
        x += i
    }

    return fmt.Sprintf(s, x)
}

func main() {
    s := []int{1, 2, 3}
    res := test("sum: %d", s...)    // Slice... Expand slice
    println(res)
}

3.3. Return value

3.3.1. Function return value

"" identifier, used to ignore a return value of a function

The return value of Go can be named and used as a variable declared at the beginning of the function body.

The name of the return value should have a certain meaning and can be used as a document.

A return statement without parameters returns the current value of each return variable. This usage is called "bare" return.

Direct return statements should only be used in short functions such as the following. In long functions, they will affect the readability of the code.

package main

import (
    "fmt"
)

func add(a, b int) (c int) {
    c = a + b
    return
}

func calc(a, b int) (sum int, avg int) {
    sum = a + b
    avg = (a + b) / 2

    return
}

func main() {
    var a, b int = 1, 2
    c := add(a, b)
    sum, avg := calc(a, b)
    fmt.Println(a, b, c, sum, avg)
}
//Output result: 1 2 3 1

Golang return values cannot receive multiple return values with container objects. Only multiple variables can be used, or '' ignored.

package main

func test() (int, int) {
    return 1, 2
}

func main() {
    // s := make([]int, 2)
    // s = test()   // Error: multiple-value test() in single-value context

    x, _ := test()
    println(x)
}
//Output result 1

3.4. Anonymous function

In go, functions can be passed or used like ordinary variables. Go language supports the definition of anonymous functions in the code at any time.

Anonymous function consists of a function declaration and a function body without a function name. The advantage of anonymous function is that it can directly use the variables in the function without declaration.

package main

import (
    "fmt"
    "math"
)

func main() {
    getSqrt := func(a float64) float64 {
        return math.Sqrt(a)
    }
    fmt.Println(getSqrt(4))
}
//Output result 2

Golang anonymous functions can be assigned to variables, used as structural fields, or transmitted in channel.

 
package main
 ​
 func main() {
     // --- function variable ---
     fn := func() { println("Hello, World!") }
     fn()
 ​
     // --- function collection ---
     fns := [](func(x int) int){
         func(x int) int { return x + 1 },
         func(x int) int { return x + 2 },
     }
     println(fns[0](100))
 ​
     // --- function as field ---
     d := struct {
         fn func() string
     }{
         fn: func() string { return "Hello, World!" },
     }
     println(d.fn())
 ​
     // --- channel of function ---
     fc := make(chan func() string, 2)
     fc <- func() string { return "Hello, World!" }
     println((<-fc)())
 }
 //Output results
     Hello, World!
     101
     Hello, World!
     Hello, World!

3.5. Closure, recursion

The Go language supports closures. Here is a brief talk about how closures are implemented in the Go language. Let me use the previous JavaScript closure example to implement closures in Go.

 package main
 ​
 import (
     "fmt"
 )
 ​
 func a() func() int {
     i := 0
     b := func() int {
         i++
         fmt.Println(i)
         return i
     }
     return b
 }
 ​
 func main() {
     c := a()
     c()
     c()
     c()
 ​
     a() //i will not be output
 }
 //Output 1 2 3

3.5.1. Recursion

Recursion is to call itself in the process of running. A function calls itself, it is called recursive function.

Conditions for recursion:

    1. The sub problem must be the same as the original problem and simpler.
    2. It is not allowed to call itself indefinitely. There must be an exit to simplify it to non recursive status processing.

Example of digital factorial:

package main

import "fmt"

func factorial(i int) int {
    if i <= 1 {
        return 1
    }
    return i * factorial(i-1)
}

func main() {
    var i int = 7
    fmt.Printf("Factorial of %d is %d\n", i, factorial(i))
}

3.6. Defer

3.6.1. defer characteristics:

    1. The keyword defer is used to register deferred calls.
    2. These calls are not executed until return. Therefore, they can be used for resource cleanup.
    3. Multiple defer statements are executed in a first in and last out manner.
    4. The variables in the defer statement are determined when the defer is declared.

3.6.2. Purpose of defer

    1. Close the file handle
    2. Lock resource release
    3. Database connection release

go language defer

The defer function of go language is powerful and very convenient for resource management, but if it is not used well, there will be traps.

defer is first in and last out

Naturally, the following statements will depend on the previous resources. Therefore, if the previous resources are released first, the following statements cannot be executed.

package main

import "fmt"

func main() {
    var whatever [5]struct{}

    for i := range whatever {
        defer fmt.Println(i)
    }
}
//Output 4 3 2 1 0
3.6.3. defer Encounter closure

 package main
 ​
 import "fmt"
 ​
 func main() {
     var whatever [5]struct{}
     for i := range whatever {
         defer func() { fmt.Println(i) }()
     }
 }
 //Output 4 4 4 4 4 4 
 /*
 In fact, go is very clear. Let's see what go spec says
 ​
 Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.
 ​
 In other words, the function executes normally. Since the variable i used in the closure has become 4 during execution, the output is all 4
 */

3.7. Exception handling

Golang does not have structured exceptions. It uses panic to throw errors and recover to catch errors.

The usage scenario of the exception is briefly described: a panic exception can be thrown in Go, and then the exception can be caught in defer through recover, and then handled normally.

panic:

      1. Built in function
       2. If a panic statement is written in function F, the code to be executed later will be terminated. If there is a list of defer functions to be executed in function F where panic is located, it will be executed in the reverse order of defer
       3. Return the caller G of function F. In G, the code after calling the function F statement will not execute. If there is a list of defer functions to be executed in function G, follow defer's reverse order.
      4. Until goroutine exits completely and reports an error

recover:

      1. Built in function
       2. It is used to control the panicking behavior of a goroutine and capture the panic, thus affecting the behavior of the application
       3. General call advice
           a) In the defer function, a goroutine panicking process is terminated by recever to resume normal code execution
           b) . you can get the error passed through panic

be careful:

      1. Use recover to process the panic instruction. Defer must be defined before panic. In addition, recover is only valid in the function called by defer. Otherwise, when a panic occurs, recover cannot capture the panic and prevent the panic from spreading.
      2. After recover handles the exception, the logic will not recover to the point of panic, and the function runs to the point after defer.
      3. Multiple defers will form a defer stack, and the later defined defer statement will be called first.
package main

func main() {
    test()
}

func test() {
    defer func() {
        if err := recover(); err != nil {
            println(err.(string)) // Transform interface {} into a concrete type.
        }
    }()

    panic("panic error!")
}
//Output result: panic error!

Sending data to a closed channel raises panic

package main

import (
    "fmt"
)

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    var ch chan int = make(chan int, 10)
    close(ch)
    ch <- 1
}
//send on closed channel

An error raised in a deferred call can be captured by subsequent deferred calls, but only the last error can be captured.

package main

import "fmt"

func test() {
    defer func() {
        fmt.Println(recover())
    }()

    defer func() {
        panic("defer panic")
    }()

    panic("test panic")
}

func main() {
    test()
}
//Output defer panic

4, Method

4.1. Method definition

The Golang method always binds an object instance and implicitly takes the instance as the first argument (receiver).

• methods can only be defined for named types within the current package.
• the parameter receiver can be named arbitrarily. If not used in the method, the parameter name can be omitted.
• the parameter receiver type can be T or * T. Base type T cannot be an interface or pointer. 
• method overloading is not supported, and the receiver is only a part of the parameter signature.
• all methods can be called by the instance value or pointer, and the compiler automatically converts them.

A method is a function that contains a receiver, which can be a value or a pointer of a named type or struct type.

All methods of a given type belong to the set of methods of that type.

4.1.1. Method definition

  func (recevier type) methodName(parameter list)(Return value list){}

    Parameters and return values can be omitted
 package main
 ​
 type Test struct{}
 ​
 // No parameter, no return value
 func (t Test) method0() {
 ​
 }
 ​
 // Single parameter, no return value
 func (t Test) method1(i int) {
 ​
 }
 ​
 // Multi parameter, no return value
 func (t Test) method2(x, y int) {
 ​
 }
 ​
 // No parameter, single return value
 func (t Test) method3() (i int) {
     return
 }
 ​
 // Multi parameter and multi return value
 func (t Test) method4(x, y int) (z int, err error) {
     return
 }
 ​
 // No parameter, no return value
 func (t *Test) method5() {
 ​
 }
 ​
 // Single parameter, no return value
 func (t *Test) method6(i int) {
 ​
 }
 ​
 // Multi parameter, no return value
 func (t *Test) method7(x, y int) {
 ​
 }
 ​
 // No parameter, single return value
 func (t *Test) method8() (i int) {
     return
 }
 ​
 // Multi parameter and multi return value
 func (t *Test) method9(x, y int) (z int, err error) {
     return
 }
 ​
 func main() {}

The following defines a structure type and a method of the type:

package main
 ​
 import (
     "fmt"
 )
 ​
 //structural morphology
 type User struct {
     Name  string
     Email string
 }
 ​
 //method
 func (u User) Notify() {
     fmt.Printf("%v : %v \n", u.Name, u.Email)
 }
 func main() {
     // Value type call method
     u1 := User{"golang", "golang@golang.com"}
     u1.Notify()
     // Pointer type calling method
     u2 := User{"go", "go@go.com"}
     u3 := &u2
     u3.Notify()
 }
 //Output results: 
 /*
     golang : golang@golang.com 
     go : go@go.com
 */
 explain:
 /*
 Explanation: first, we define a structure type called User, and then define a method of this type called Notify. The receiver of this method is a value of User type. To call the Notify method, we need a User type value or pointer.
 ​
 In this example, when we use the pointer, Go adjusts the dereference pointer so that the call can be executed. Note that when the receiver is not a pointer, the method operates on the copy of the value of the corresponding receiver (that is, even if you call the function with a pointer, the receiver of the function is a value type, so the internal operation of the function is still the operation on the copy, not the pointer operation).
 */

Modify the Notify method so that its receiver uses the pointer type

 package main
 ​
 import (
     "fmt"
 )
 ​
 //structural morphology
 type User struct {
     Name  string
     Email string
 }
 ​
 //method
 func (u *User) Notify() {
     fmt.Printf("%v : %v \n", u.Name, u.Email)
 }
 func main() {
     // Value type call method
     u1 := User{"golang", "golang@golang.com"}
     u1.Notify()
     // Pointer type calling method
     u2 := User{"go", "go@go.com"}
     u3 := &u2
     u3.Notify()
 }
 //Output results:
 /*
     golang : golang@golang.com 
     go : go@go.com
     Note: when the receiver is a pointer, that is, it is called with a value type, the function also operates on the pointer.
 */

4.2. Differences between ordinary functions and methods

1. For ordinary functions, when the receiver is of value type, the data of pointer type cannot be passed directly, and vice versa.

2. For methods (such as struct methods), when the receiver is a value type, you can directly call the method with a pointer type variable, and vice versa.

 package main
 ​
 //The difference between ordinary functions and methods (when the receiver is value type and pointer type respectively)
 ​
 import (
     "fmt"
 )
 ​
 //1. Ordinary function
 //Function that receives a value type parameter
 func valueIntTest(a int) int {
     return a + 10
 }
 ​
 //Function that receives pointer type parameters
 func pointerIntTest(a *int) int {
     return *a + 10
 }
 ​
 func structTestValue() {
     a := 2
     fmt.Println("valueIntTest:", valueIntTest(a))
     //If the parameter of a function is a value type, you cannot directly pass a pointer as a parameter
     //fmt.Println("valueIntTest:", valueIntTest(&a))
     //compile error: cannot use &a (type *int) as type int in function argument
 ​
     b := 5
     fmt.Println("pointerIntTest:", pointerIntTest(&b))
     //Similarly, when the parameter of a function is a pointer type, the value type cannot be directly passed as a parameter
     //fmt.Println("pointerIntTest:", pointerIntTest(b))
     //compile error:cannot use b (type int) as type *int in function argument
 }
 ​
 //2. Method
 type PersonD struct {
     id   int
     name string
 }
 ​
 //Receiver is value type
 func (p PersonD) valueShowName() {
     fmt.Println(p.name)
 }
 ​
 //Receiver is of pointer type
 func (p *PersonD) pointShowName() {
     fmt.Println(p.name)
 }
 ​
 func structTestFunc() {
     //Value type call method
     personValue := PersonD{101, "hello world"}
     personValue.valueShowName()
     personValue.pointShowName()
 ​
     //Pointer type calling method
     personPointer := &PersonD{102, "hello golang"}
     personPointer.valueShowName()
     personPointer.pointShowName()
 ​
     //Different from ordinary functions, the receiver is a method of pointer type and value type, and variables of pointer type and value type can be called each other
 }
 ​
 func main() {
     structTestValue()
     structTestFunc()
 }
 ​
 /*
     Output results:
         valueIntTest: 12
         pointerIntTest: 15
         hello world
         hello world
         hello golang
         hello golang
 */

Tags: Go

Posted on Sat, 23 Oct 2021 20:35:59 -0400 by Nandini