golang learning notes basic summary!

Posted by avo on Mon, 25 Oct 2021 11:46:57 +0200

golang study notes

  • Opening: first study, understanding is still shallow. For the time being, take some notes and enrich them slowly in the future, and then correct or add them~

Chapter I: Enlightenment and environmental installation

  • Installation link (non official website): https://studygolang.com/dl

  • Terminal input: go env to view the image resource

  • Change a software download source:

    • windows: go env -w GOPROXY=https://goproxy.cn,direct
    • linux: export GOPROXY=https://goproxy.cn
  • Set GO111MODULE to on: go env -w GO111MODULE=on

    • GOPATH: the previous package management tool will be mentioned later
    • GOMODULE: current package management tool
  • Installation tool: go get -g -v golang.org/x/tools/cmd/goimports

  • Installing go language editor: GoLand

  • Click settings to configure the relevant environment.

    • GOROOT: click and the go you installed will automatically appear. Select it. (note! GoLand should not recognize Chinese. It was installed on Disk C by default. I guess this is the reason. I can't find the go installation location. Then install a go in the pure English path of disk d to display ~)
    • GPPATH: as will be mentioned later, there are requirements for directory structure. (configure it casually. For example, create a folder called gopath and another folder called src)
    • GoModules: this configuration is the mainstream GO111MODULE. At present, go depends on management. Select the corresponding Vgo, and then fill in Environment: https://goproxy.cn,direct changes the download source.
  • Create project:

      1. Set proxy: settings - > go - > Go Modules (VGO), check Enable Go Modules (vgo) integration to enable Go Modules, and enter in the environment input box:

         https://goproxy.cn,direct
        
      1. Set settings - > go - > gopath, and check index entity gopath to index the entire gopath. You can also create a gopath for the project alone. (if you use go Modules, you don't need to configure this. I think ~)
      1. Go, Go Modules, Dep, App Engine: select GO Modules
        • Location: set the project directory.
  • a minor question

    // fmt.Println(a...: 123) this a... Is actually the name of the println parameter
    
    // It's shown by goland. You can turn it off in settings 
    
    // 1. Search for parameter hint in the setting. In 2020.1.1, Go under Inlay Hints. Click Show parameter hint and apply it.
    
  • To use vscode, you need to install a bunch of plug-ins (automatic prompt for installation)

    • Command line go run + filename execution
    • go mod init + module name to generate mod file
    • (in addition) go build compilation file

Chapter two: basic grammar

1. Method of defining variables:

package main

// Variables can also be defined outside the function, but only var can be used
// In addition, this is a variable within the package (the scope is within the package), and there is no global variable
var aa = 33
// You can also write this way
var (
	bb int = 1
	cc string = "123"
	dd float32 = 2.01
)

func variable() {
	var a, d int = 1, 2
    // Var a, d = 1, 2 can also be assigned according to the type assigned later. There is no need to declare it. The compiler can infer the variable type
    // var a, b, c , s = 3, 4, true, "def"
    // P: = 10 can also replace the var keyword
	var b int
	var s string = "123"
	fmt.Println(a, b, s)
	fmt.Printf("%d %q\n", a, s)  // Print in format, and the defined variables must be used!
    
    fmt.Println(aa)
}

2. Built in variable type

  • bool, string
  • (u)int, (u)int8, (u)int16, (u)int32, (u)int64, uinputer
  • Byte, run (character type, for Multi Country applications, 32 bits in length, 4 bytes, unicode)
  • float32, float64, complex64, complex128 (imaginary number, real part, imaginary part, 128 bits each)
  • Type is cast
    • var a, b = int = 3, 4
    • var c int = math.Sqrt(a * a + b * b) error
    • var c int = int(math.Sqrt(float64(a*a + b * b))) correct
  • In addition, the float type is inaccurate, every language exists, and binary cannot accurately represent decimal

3. Constant definition

func consts() {
	const filename string = "123.txt"
    // Displays the indicated type
	const a, b float32 = 3, 4
	const b float32 = 4
    // float64 must be added here
	var c int = int(math.Sqrt(float64(a*a + b*b)))
	fmt.Println(a, b, filename, c)
}


func consts() {
	const filename string = "123.txt"
	const a, b = 3, 4
    // In this way, if a and b are text, they will be used as float s~ 
	var c int = int(math.Sqrt(a*a + b*b))
	fmt.Println(a, b, filename, c)
}

// Enumeration type
func enums() {
	const(
		cpp = 0   // app = iota means self increment. Complex values are not required for the following_ You can skip a value. You say java doesn't want it. Its position is written as_
		java = 1
		python = 2
		golang = 3
	)
    
    // Using iota to realize complex
    const (
		b = 1 << (10 * iota)
		kb
		mb
		gb
		pb
	)
    // The results are calculated according to the formula
    1 1024 1048576 1073741824 1099511627776
}

4. Conditional statements

  • if else statement

    if condition{
        ...
    } else if {
        ...
    } else {
        ...
    }
    
    // Conditional assignment is supported, and the scope of the variable is in the if else statement
    if a, b := 2, 3; a == 1{
        fmt.Println(a)
    } else {
        fmt.Println(b)
        fmt.Println("cannot print", nil)
    }
    
    
  • switch statement

    // The switch statement does not need to add a break after every sentence, and there is a break after every case, unless fallthrough is used
    var result int
    var op = 1
    switch op {
    case 1:
        result = 1
        // Program execution is interrupted and an error panic is reported
        panic("error")
    case 2:
        result = 2
    case 3:
        result = 3
    default:
        result = 4
    }
    
    
  • for statement

    sum := 0
    for a := 1; i <= 100; i++ {
        sum += i
    }
    
// Complex read files and print them line by line
func forT(filename string) {

	file, err := os.Open(filename)

    // Judge whether it is abnormal

	if err != nil {

		panic(err)

	}

	scanner := bufio.NewScanner(file)

   	// There is only this end condition. Omit directly; (neither the start condition nor the increment condition) is used as a while

	for scanner.Scan() {

		fmt.Println(scanner.Text())

	}

 // The increment condition can also be omitted. It is equivalent to an endless loop
  for {
      fmt.Println("abc")
  }

5. Function

// 1. Return single value
  func eval(a, b int, op string) int {}

  // 2. Return two int values
  func div(a, b int, op string) (int, int) {
      return a / b, a % b
  }

  // 3. You can also name the return value: 
  func div(a, b int, op string) (q, r int) {
      return a / b, a % b
  }

  // When the function is executed, it can be generated automatically
  q, r := div(13, 3)

  // 4. When defined, it will return directly and automatically
  func div(a, b int, op string) (q, r int) {
      q := a / b
      r := a % b
      return  // Automatically return q, r: This is not recommended. When the code is long, the readability is too poor
  }


  // 5. according to the above example, call in other functions.
  // This is an error because these are the two returned values. return div(a, b)[0] means to return the first value like python
  return div(a, b) 

  // This is also wrong! ha-ha.. Two variables defined in go language must be used to avoid compilation errors
  q, r := div(a, b)
  return q

  // Correct writing_ Indicates that this value is not required
  q, _  := div(a, b)
  return q

  // 6. Generally speaking, multiple return values are not used indiscriminately. The second return value is generally the returned error, such as file, err: = OS. Open (filename)
  func div(a, b int, op string) (int, error) {
      if a == 1 {
          return 1, nil
      } else {
          return 0, fmt.Errorf("err....")  // When an error occurs, an exception error is returned
      }
  }


  aaa, err := div(2, 3, "123")
  fmt.Println(aaa, err)
  // The results will be printed out
  // 0 err....123

  // 7. Function as formal parameter
  // op is a function with two int type parameters, and the return value is int type
  func apply(op func(int, int) int, a, b int) {
      return op(a, b)
      
      // You can also get the original function name
      p := reflect.ValueOf(op).Pointer() // Gets the real pointer to the function
      opName := runtime.FuncForPc(p).Name() // Get function name
  }

  // 8. Anonymous function
  // apply is a function. apply (func (int, int) int, a, B, int) is called below. The intermediate function parameter transfer is an anonymous function, which is defined internally
  apply(
      func(a int, b int) int {
          return a
      }, 3, 4)
  )


// Function parameters: there are no default parameters, optional parameters, function overloading and other complex function parameter types in go language
// There is a list of variable parameters
func sum(numbers ...int) int{  // numbers can accept multiple int values
    s := 0 
    for i := range numbers {
        s += numsbers[i]
    }
    return s
}
// implement
sum(1, 2, 3, 4, 5)

6. Pointer

// In C language
int a = 100;
// In the first line of code, * is used to indicate that p is a pointer variable, and in the second line of code, * is used to obtain the data pointed to by the pointer.
int *p_a = &a;
*p_a = 200;

// go in
var a int = 2
var pa *int =&a
*pa = 3

// The pointer in go language is simple: it can no longer be operated. In C language, the header pointer of the list can be added continuously.

  • Parameter transfer in function: there is only one transfer method of value transfer in go language!

    • In go language, value transfer and pointer transfer can also be carried out. Equivalent to the function of reference.

      // Pointers work better than references. Generally speaking, the basic built-in types of java and Python are value passing, for example:
      // In Python, a = 5 is passed into the function and will not affect the value of the variable. This is value passing.
      // list, container objects and custom type objects are all passed by reference
      
      // In the go language, pointer passing. 
      var a int
      func f(pa *int){}  // Passed &a in, the variable can be modified in the function even if it is of type int
       Take a simple example: swap two numbers
      var a, b int = 3, 4
      // Value transfer cannot be exchanged, but the values of a and b are copied. The formal parameters are also called a and b
      func swap(a, b int) {
          a, b = b, a
      }
      swap(a, b)
      // Pointer passing: can be exchanged correctly
      func swap(a, b *int) {
          *a, *b = *b, *a
      }
      swap(&a, &b)
      
      // When an object is passed, the value is also passed. 
      var cache Cache
      func f(cache Cache){}
      
      // Cache is an object of type cache. It does not store real data in memory, but pointers to data in memory, 
      // Therefore, there is no problem with value passing. It is equivalent to copying the pointer of the object and operating on the same object
      
    • So when you see an object, should you pass it by value type or pointer type? In object encapsulation.

Chapter 3: built in container

(1) Array

1. Array

func array() {
	// One bit array definition
	var arr1 [5]int
	arr2 := [3]int {1, 2, 3}
	arr3 := [...]int{2, 4, 6, 8 ,10}

	// Two dimensional array: 4 rows and 5 columns
	var grid [4][5]int

	fmt.Println(arr1)
	fmt.Println(arr2)
	fmt.Println(arr3)
	fmt.Println(grid)

	// 1. Traversal
	for i:= 0; i < len(arr3); i++{
		fmt.Print(arr3[i], " ")
	}

	// 2. Traversal: range
	fmt.Print("\n")
	for i := range arr3 {
		fmt.Println(arr3[i], " ")
	}

	// Equivalent to enumerate() in python, traversing indexes and values
	// If you only want the value, you can for, V: = range arr3, underlined instead
	for i, v := range arr3 {
		fmt.Println(i, v)
	}

	// Why use range? 1. Simple and beautiful. two 
}
  • Array is a value type!

    // arr [5]int passes a value type like this. That is, the passed in array must be 5 in length.
    func printArray(arr [5]int) {
        arr[0] = 100
        for i, v := range arr {
    		fmt.Println(i, v)
    	}
    }
    
    printArray(arr1)  // yes
    printArray(arr2)  // error
    
    // Because it is value transfer, it is equivalent to making a copy of the five elements of the array as a whole, and then changing the array in the function will not affect the outside!
    
    // If you want to change the original array in other languages, you need to pass a pointer
    func printArray(arr *[5]int) {} // In the array, the value of the external array can be modified by arr[0] = 100
    printArray(&arr1)
    
  • In the go language, arrays are not used directly: instead, slices are used~

2. slice

arr := []int {1, 2, 3, 4 ,5 ,6 ,7 ,8}  // Pay attention to the difference between arrays and arrays. Don't add
// 1. The slice here is the same as python, s = [2, 3, 4, 5], closed on the left and open on the right
s := arr[2:6]  

// 2. Other writing methods: arr[:6], arr[2:], arr [:] are the same as python

// 3. slice is not a value type. It has a data structure inside. slice is a view of arr to see the contents specified in the array
// Pass the slice of the array to the function, and modify it to change the external array
func updateSlice(s []int) {
    s[0] = 100
}

updateSlice(s)
fmt.Println(arr)  // It will become 100

// 4. If you want to operate on the original array, you can use slice slice
updateSlice(arr[:])  

// 5. Slices can be sliced. The result is a slice based on the current slice
arr := [...]int {1, 2, 3, 4 ,5 ,6 ,7 ,8}
s1 := arr[2:6]  // s = [3, 4, 5, 6]
s2 := s1[3:5]	// s = [6 ,7]
s3 := s1[3:]   // s = [6]

// You can see that you are over fetching, because the bottom layer is the view of arr. When s2 specifies [3:5] exceeds s1, the hidden part will be fetched
// s3 takes s1[3:]. By default, you can only see the part of s1 displayed, that is, only one 6 is taken


// 2D slice
var a [][]string
// perhaps 
b = make([][]string, 0)

// assignment
a = [][]string{[]string{"abc", "efg"}, []string{"abc", "mln"}}
fmt.Println(a)

// result
[[abc efg] [abc mln]]

3. Underlying principle of slicing

slice
param: ptr Points to the beginning element of the slice
param: len Current slice length, when s[6] If the length is greater than this, an error will be reported.
parma: cap(capacity), Pointer pair ptr How long is it to the end.

// Note: 
// 1. slice can be extended backward, but cannot be extended forward. When the first element of the slice is greater than len, an error will be reported. s2 = s1[6:7] will report an error and will not be extended
// 2. You can print out and obtain the values of len and cap
fmt.Printf("s1= %v, len(s1)=%d, cap(s1)= %d\n", s1, len(s1), cap(s1))
// Results can be obtained
s1= [3 4 5 6], len(s1)=4, cap(s1)= 6

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-X79rhUOh-1635157982890)(.\slice.png)]

4. Add elements to slice

arr := [...]int {1, 2, 3, 4 ,5 ,6 ,7 ,8}
s1 := arr[2:6]
fmt.Printf("s1= %v, len(s1)=%d, cap(s1)= %d\n", s1, len(s1), cap(s1))
s2 := append(s1, 10)
s3 := append(s2, 11)
s4 := append(s2, 12)
s5 := append(s4, 13)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
fmt.Println(s5)
fmt.Println(arr)

// The results are as follows:
/* 
s1= [3 4 5 6], len(s1)=4, cap(s1)= 6 
[3 4 5 6]
[3 4 5 6 10]
[3 4 5 6 10 11]
[3 4 5 6 10 11 12]
[3 4 5 6 10 11 12 13]
[1 2 3 4 5 6 10 11]

It can be seen that the maximum result of s1 is cap = 6, so in the later operation of slicing, 10 and 11 cover 7 and 8 in arr.
Inserting 12 and 13 in the back will not affect the arr. 

Summary: when adding elements, if the cap is exceeded, the system will reallocate a larger underlying array and copy the original elements (refer to the underlying structure above)

In addition: due to value passing, you must receive the return value of append. Because when append, the len, cap, etc. in the underlying array may change, and new returns need to be received again
*/

5. Other operations of slice: create, delete, and copy

  • Create slice
// 1. Declare the variable type as slice
var s []int   // The default value is nil, and the variable definition in go is not initialized

// 2. The second method: create an array, and there is a slice that declares that s1 is an array
s1 := []int {2, 4, 6, 8}  

// 3. Create a slice with a fixed length
s2 := make([]int, 16, 32) // len = 16, cap = 32, not value when cap, the default is len length

// 4. You can append
for i := 0; i < 100; i++ {
    fmt.Printf("len(s)=%d, cap(s)= %d\n", len(s), cap(s))
    s = append(s, i * 2 + 1)
}

// Each expansion of the underlying array is multiplied by 2. The printout can be seen
len(s)=0, cap(s)= 0
len(s)=1, cap(s)= 1
len(s)=2, cap(s)= 2
len(s)=3, cap(s)= 4
len(s)=4, cap(s)= 4
len(s)=5, cap(s)= 8
len(s)=6, cap(s)= 8
len(s)=7, cap(s)= 8
len(s)=8, cap(s)= 8
len(s)=9, cap(s)= 16
len(s)=10, cap(s)= 16
len(s)=11, cap(s)= 16
len(s)=12, cap(s)= 16
len(s)=13, cap(s)= 16
len(s)=14, cap(s)= 16
len(s)=15, cap(s)= 16
len(s)=16, cap(s)= 16
len(s)=17, cap(s)= 32
len(s)=18, cap(s)= 32
len(s)=19, cap(s)= 32

//5. copy: copy s1 to s2
copy(s2, s1) 

// 6. delete
// Delete element with index value 3: s2[:3] + s2[4:]
// The second value of append is a variable length parameter list, that is, multiple values can be added at one time. When adding a slice, add... After it
// In addition, deleting an element does not affect the cap. The size remains the same, len - 1
s2 = append(s2[:3], s2[4:]...)   

// 7. Delete the element of the header
front := s2[0]
s2 := [1:]

// 8. Delete tail element
tail := s2[len(s2 - 1)]
s2 := s2[:len(s2) - 1]

(2) Map

###1. Basic content

// 1. Create a map
// Method 1
m := map[string] string {
    "name": "xiaoming",
    "course": "golang",
    "age": "18",
}
 
// Method 2. nil (equivalent to none in Python) in go language can participate in operation, which is equivalent to the use of empty map
m1 := make(map[string]int) // m1 == empty map empty map
var m2 map[string] int  // m2 == nil empty

// 2. When traversing the map, you can also use underscores_ Replace what you don't want
// Note: the order of each traversal is not necessarily the same because of the structure of the map
for k, v := range m {
		fmt.Println(k, v)
	}

// 3. Get value
courseName := m["course"]
// When you get a non-existent value, you get an empty string, which corresponds to the null value of the data type. go language variables can be used without initialization
  
courseName, exist := m["course"]  // The second variable exist can judge whether the current value exists and return the bool value

// standard notation 
if courseName, exist := m["course"]; exist {
    fmt.Println(courseName)
} else {
    fmt.Println("error")
}

// 4. Delete element
delete(m, "name")

2. Data type of map key value pair

  • The map uses a hash table and must be comparable
  • Except slice, map and function, all built-in types can be key
  • The Struct type does not contain the above fields and can also be used as a key. (checked during compilation)

3. Real exercise

import "fmt"
func lengthOfLongestSubstring(s string) int {
    charMap := map[byte] int {}
    i := 0
    rst := 0
    byteS := []byte(s)    // Convert to byte slice

    for j := 0; j < len(byteS); j++ {
        if _, ok := charMap[byteS[j]]; !ok {
            charMap[byteS[j]] = 1    
        } else {
            // The current element appears repeatedly. Calculate the current window size first
            if rst < j - i {
                rst = j - i
            }
            // Remove duplication
            for _, ok := charMap[byteS[j]]; ok ; _, ok = charMap[byteS[j]]{
                delete(charMap, byteS[i])   
                i += 1
            }
            // Add current element to window
            charMap[byteS[j]] = 1 
        }
    }
    // Finally, there will be a position without judgment
    if rst < len(byteS) - i{
        return len(byteS) - i
    } else {
        return rst
    }
}


// Ha ha, it's true. The brain hole is wide open. Remove the repetition. It should be a while loop. If you write with for, you should understand it; What are the three parts? Let me write them right
// If s[j] is in the map, it will loop all the time, i++
for charMap[s[j]]; ok ; _, ok = charMap[s[j]]{}
// You can also do this directly: by default, 0 is returned if the element is not found
for charMap[s[j]] != 0 {}

// Note:
// 1. I and j variables are defined at the beginning to be used in the whole code!, for can only be used in the corresponding {} code block    
// 2. Charmap: = map [byte] int {} pay attention to this definition. go language handles characters

// In addition, it should be noted that

###(3) Character and character processing

1. Brief description

func strings() {
	s := "ABC I'm a beginner!" // UTF-8 encoding, rune type
	fmt.Println(s, len(s))  // Print length is 19! 
    

	// 1. Convert to byte type output
	for _, b := range []byte(s) {
		fmt.Printf("%X ", b )
	}
	// The one byte representation is printed as follows: 3 bytes for Chinese
	// For example, E6 88 91 means "I", which is UTF-8 coding
	// 41 42 43 E6 88 91 E6 98 AF E5 88 9D E5 AD A6 E8 80 85 21
	fmt.Println()	

	// 2. Direct output
	for i, ch := range s {
		fmt.Printf("(%d, %X)", i, ch)
	}
	// Results: (0, 41)(1, 42)(2, 43)(3, 6211)(6, 662F)(9, 521D)(12, 5B66)(15, 8005)(18, 21) 

	// ch is a four byte integer of type rune (equivalent to int32).
	// 6211 means "I", which is unicode coding. And you can see that the subscript is incremented by three.
	// "I": process, decode the s(string) character utf-8: E6 88 91, convert it to unicode 6211, and then save it in the rune type four byte type
	
	fmt.Println()

	// 3. Call utf8 this library
	fmt.Println(utf8.RuneCountInString(s))
	// The print result is 9 

	// Get byte array
	bytes := []byte(s)
	// Return each character and character size, English 1, Chinese 3. decode
	for len(bytes) > 0 {
		ch, size := utf8.DecodeRune(bytes)
		bytes = bytes[size:]
		fmt.Printf("%c ", ch)
	}
	// Results A B C I'm a beginner!
	fmt.Println()

	// Convert to rune type
	for i, ch := range []rune(s) {
		fmt.Printf("(%d, %c)", i, ch)
	}
	// Results (0, A)(1, B)(2, C)(3, I) (4, yes) (5, beginning) (6, learning) (7, person) (8,!)
}
  • Summary:
    • Use range to traverse pos and run pair
    • Use utf8.RuneCountInString() to get the number of characters
    • Get byte length using len
    • Get bytes using [] byte
  • Let's sort out the above content
package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	s := "ABC I just started learning go!"
	var runeS []rune = []rune(s)   // Print 11, each rune is int32, 4 bytes
	fmt.Println(len(runeS))

	var byteS []byte = []byte(s)  // Print 21, the Chinese character set occupies three bytes
	fmt.Println(len(byteS))
	
	// Return each character and character size, English 1, Chinese 3. decode
	for len(byteS) > 0 {
		ch, size := utf8.DecodeRune(byteS)
		byteS = byteS[size:]
		fmt.Printf("%c ", ch)
	}
	// Results A B C I'm a beginner!
	
	// 3. Call utf8 to get the desired length 11
	fmt.Println(utf8.RuneCountInString(s))
}


// Note: A: = [] byte (s) or a: = [] run (s) or s (strings are of run type by default)
// They use a [2] and s [2] to get numbers, the special representation of strings in go

2. True title: International Version (support Chinese)

// The byte slice is represented as the character slice (run)
func lengthOfLongestSubstring(s string) int {

    charMap := map[rune] int {}
    i := 0
    rst := 0
    runeS := []rune(s)

    for j, ch := range runeS {   // Here ch is also a number, or directly for J: = 0; j < len(runsS);  j++
        if _, ok := charMap[ch]; !ok {
            charMap[ch] = 1    
        } else {
            // The current element appears repeatedly. Calculate the current window size first
            if rst < j - i {
                rst = j - i
            }
            // Remove duplication
            for charMap[ch] != 0{
                delete(charMap, runeS[i])   
                i += 1
            }
            // Add current element to window
            charMap[ch] = 1 
        }
    }
    // Finally, there will be a position without judgment
    if rst < len(runeS) - i{
        return len(runeS) - i 
    } else {
        return rst
    }
}
  • Finally, let's look at the comparison results:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-lkl44vpr-1635157982892) (. / run_byte_range. PNG)]

  • Note that it is only used as a performance reference, which is not accurate. I tried it several times, but the time is not bad.

3. Other string operations

// There are many operations under the strings library
 The teacher didn't speak in detail, so I'll record it later
  • summary
    • Strings are separated or combined: Fields, Split, Join
    • Contains, index: find substrings
    • ToLower, ToUpper:
    • Trim, TrimRight, TrimLeft

Chapter 4: structure and method "object oriented"

###1. Basic content

  • Object oriented characteristics of go language
    • Only encapsulation is supported, inheritance and polymorphism are not supported
    • go language has no class, only struct
  • definition
// Defines the node type of a tree
type TreeNode struct {
    Left, Right *TreeNode  // Pointer type
    Value int
}
  • establish
func main() {
	// 1. Definitions
	var root1 TreeNode
	root2 := TreeNode{}

	// initialization
	root1 = TreeNode{val: 3}

	root1.left = &root2  // Pay attention to the pointer and pass the address
    // Note that in go language, both instances and attributes are. For example, c + + may be - > 
	root1.right = &TreeNode{val: 4}

	fmt.Println(root1.val)
    
    // 2. You can also define a slice
    nodes := []TreeNode {
		// Omit initialization data
		{val: 3},
		{},
		{nil, &root1, 6},
	}

	fmt.Println(nodes)
    //Results: [{< nil > < nil > 3} {< nil > < nil > 0} {< nil > 0xc000004078 6}]
}

###2. Constructor?

  • A structure has no constructor.

    • Because there are many initialization methods described above. But sometimes we need to control its structure
    • You can use factory functions, which are actually ordinary functions, such as
    // Factory function
    func createNode(val int) *TreeNode{
    	return &TreeNode{val: val}
    }
     
    // After learning c + +, you can see a typical error here. TreeNode{val: val} is a local variable built in a function
    // Return the pointer of the local variable, and the program will hang up!
    // go language will not hang ~ local variables can also be returned to others
    
    root1.left.right = createNode(9)  // This is also possible
    

3. Creation of structure (stack area)

  • Since the address of the local variable is returned as discussed above, where does this TreeNode exist?
    • For example, in c + +, local variables are allocated on the stack. As soon as the function exits, the local variables will be destroyed immediately
    • If you want to pass it out, you need to allocate it on the heap and release it manually. This is c + +.
    • In java, generally, objects are in the heap and variables are in the stack.
    • In go language:
      • No need to know ~ because there is a garbage collector. It needs to be determined according to the editor of go language and the running environment.
      • As in the above example, when the compiler finds that TreeNode{val: val} does not take an address and returns, it feels that it does not need to be used for the outside world and may be allocated in the temporary stack.
      • When an address is taken (like the example above) and returned, it will be allocated on the heap. After allocation on the heap, it will participate in garbage collection. When the pointer passed to the outside world is "thrown away", the structure object will be recycled.
    • Memory structure diagram
  • [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ponh9NUs-1635157982893)(./struct.png)]

4. Define method for structure

// TreeNode structure
type TreeNode struct {
	left, right *TreeNode
	val int
}

// Define method for structure
func (node TreeNode) print() {  // (node TreeNode) recipient
	fmt.Println(node.val)
}

// call 
root1.print()
  • There is a receiver, which is node TreeNode.

  • In fact, the definition of go language is similar to the ordinary function method. It is just a syntax. In fact, it is an ordinary function, which is equivalent to:

    func print(node TreeNode) {  // Here is value transfer, go only value transfer
    	fmt.Println(node.val)
    }
    
    // call
    print(root1)
    
  • It should be noted that the above writing methods are value transfer. Internal modification of the function will not affect external objects of the heap, which is equal to copy.

    • It needs to be written as follows:
    func (node *TreeNode) print() {  // Pass pointer
    	fmt.Println(node.val)
    }
    
    // Call: This is more humanized. Although it becomes a pass pointer, the call remains unchanged. The actual call will automatically pass in the address
    root1.print()
    
    // Similarly, if the function definition is to pass a value, you pass in an address ~ and it will not affect it. It will automatically take out and copy the value from the address.
    
  • Summary: go is very smart. You know whether you want a value or a pointer when calling a function.

    • Nil pointer can also call methods! It is different from other languages because go sometimes initializes empty objects and sometimes nil. But it can be used.

type TreeNode struct {
left, right *TreeNode
val int
}

//Define method for structure
Func (node * treenode) print() {/ / (node treenode) receiver
fmt.Println(node.val)
}

func (node *TreeNode) setValue(val int) {
if node == nil {
fmt.Println("Setting val to nil node!")
}
node.val = val
}

var pRoot *TreeNode
//nil pointer, calling method
pRoot.setValue(200) / / you can print it here, indicating that the method can be called, but the following node.val = val operation cannot be performed~
pRoot = &root1
pRoot.setValue(300)
pRoot.print()

* For the value receiver, the pointer receiver's problem

* To change content, you must use a pointer receiver
* If the structure is too large, consider using the pointer receiver
* Consistency: if there are pointer receivers, it is better to be pointer receivers.
* Value recipient is go Unique. Pointer receiver, c++, java quote, python(self),It's also like a pointer.

### 5. Implement a method of traversing binary tree

* Preorder traversal: [Force buckle original question](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/submissions/)

```GO
/**
* Definition for a binary tree node.
* type TreeNode struct {
*     Val int
*     Left *TreeNode
*     Right *TreeNode
* }
*/
// Recursive problem solving
func preorderTraversal(root *TreeNode) (vals []int) { // This is equivalent to defining the variable vals
  var preorder func(*TreeNode)
  preorder = func(node *TreeNode) {
      if node == nil {
          return
      }
      vals = append(vals, node.Val)
      preorder(node.Left)
      preorder(node.Right)
  }
  preorder(root)
  return
}


// In addition, it should be noted that slice is initialized
zxc := []int{}
var cvb []int
fmt.Println(zxc == nil, cvb == nil)  // Result false, true
  • The focus is on the nil pointer and calling the method. If the above method is implemented as a TreeNode method, in other languages, an error will occur when node == nil. Nil in go language can call the function under internal judgment.
// Method written as TreeNode
func (node *TreeNode) traverse() {
    if node == nil {
        return
    }
    
    node.print()
    node.left.traverse()
    node.right.traverse()
}

###6. Package and packaging

  • By naming the function, you can distinguish whether the function is visible or invisible. CamelCase is usually used for names: words are capitalized together.

  • For packages:

    • Initial capital: public
    • Initial lowercase: private
  • Package:

    • One package per directory: the package name does not have to be the same as the directory name, but each directory can only have one package
    • The main package contains executable entries and main functions. If there is a main function in the directory, there can only be one main package in this directory.
    • The methods defined for the structure must be placed in the same package.
    • You can make different files
    tree catalogue
    	node.go //Define method and node structure package tree
    	traverse.go // For clarity, you can also separate the methods of traversing nodes into a separate file. Can be found
    	entry // catalogue
    		entry.go  // Move the main function here, package main: there can only be one package in a directory. To create a new directory, name it package main, and write in the main function.
    		// The import statement in entry.go imports the tree directory.
    
    // The import statement in entry.go imports the tree directory. 
    // tree.TreeNode is required when creating variables
    var root tree.TreeNode
    
  • Something about the guide bag (I feel it):

    • First, the package name is not necessarily the same as the directory name where the package is located.
    • Then, when the package is imported, it is the imported directory name
    // directory structure
    01
    	testing
    		retriever.go  // The package name is package testing111, which defines a type Retriever struct {}
    	downloader.go   // Import the package and call the contents of the package
    
    import "01/testing"   // Import to pathname
    
    testing111.Retriever{}  // Call using package name
    
  • In addition, structures and methods defined in another package can only be defined in the same package, otherwise an error will be reported: invalid receiver *testing111.Retriever (type not defined in this package)

  • Create another file in the testing directory above. Similarly, the method can be successfully defined in package testing111 because it belongs to the same package.

7. Extend existing types

  • In other languages, there is the concept of inheritance, which can extend existing types

  • There is no inheritance in the go language

  • go can extend system types and other types in two ways:

    • The following code. According to the package structure defined above (tree directory)

      1. Use combination:
    // (according to the go language habit, the package is called tree, and TreeNode is rewritten as node, tree. TreeNode, which is too repetitive)
        // And the initial capital letter indicates the method of Public. All method names are capitalized.
        type Node struct {
        	Val int
        	Left *TreeNode
        	Right *TreeNode
        }
    
        // Objective: make Node objects support medium order traversal (Node implements pre order traversal)
    
        // Define your own node
        type MyTreeNode struct {
        	node *tree.Node // Saves a pointer to the node type.
        }
    
        func (myNode *MyTreeNode) InOrder() {
            if myNode == nil || myNode.node == nil { // If it is empty, or the node of the package is empty, it will not proceed.
                return
            }
            
         	// You need to wrap the Node as myTreeNode and call the middle order traversal method for middle order traversal
            left := myTreeNode{myNode.node.Left} 
            right := myTreeNode{myNode.node.right} // myTreeNode{myNode.node.Left}.InOrder() cannot be written like this here because it passes a pointer and must be received with a variable
            left.InOrder()
            myNode.node.Print()
            right.InOrder()
        }
    
      // Display of error message: myTreeNode{myNode.node.Left}.InOrder()
      /*
      	cannot call pointer method on myTreeNode literal
       	cannot take the address of myTreeNode literal
       	Cannot call pointer method on myTreeNode literal
        	Cannot get the address of myTreeNode text
      */
    
    1. By alias
      Implement a queue.
    
      // Define a Queue. The directory structure is as follows
      queue // catalogue
      	entry // catalogue
      		main.go // Program entry
      	queue.go  // Defining structures and methods
    
  • queue.go

package queue

// Define a queue
type Queue []int

func (q *Queue) Push(v int) {
	// go language features, pointers can be changed. Q passes in a pointer, * q operates the object it points to
	// Then q points to the new object, and then points to the newly generated object of append
	// *Q is equivalent to self in python, representing the object itself, but in go, what self (q) points to is changed into a newly generated object ~!
	*q = append(*q, v)
}

// First in first out queue
func (q *Queue) Pop() int {
	head := (*q)[0]
	*q = (*q)[1:]
	return head
}

func (q *Queue) IsEmpty() bool {
	return len(*q) == 0
}
  • main.go
package main

import (
	"fmt"
	"01/queue"
)

func main() {
	q := queue.Queue{1}
	q.Push(2)
	q.Push(3)
	fmt.Println(q.Pop())
	fmt.Println(q.Pop())
	fmt.Println(q.IsEmpty())
	fmt.Println(q.Pop())
	fmt.Println(q.IsEmpty())
}
  • The print result is:
1
2
false
3
true
// Meet expectations

8. Use inline to extend existing types

  • Embedding: Embedded
// The previous combination:
type MyTreeNode struct {
	node *tree.Node // Saves a pointer to the node type.
}

// Embedding is to remove node, which is equivalent to a syntax sugar of go. It saves some code and is simpler
type MyTreeNode struct {
	*tree.Node // Saves a pointer to the node type.
}

// The advantage of embedding is to save code. Look at the code in the sequence traversal in the previous combination
left := myTreeNode{myNode.node.Left} 
right := myTreeNode{myNode.node.Left}

// After using embedded, myNode has all the properties and methods of the original Node, as well as its own defined properties and methods.
// Write directly, myNode.Left, omitting the middle node
left := myTreeNode{myNode.Left} 
right := myTreeNode{myNode.right}
  • Summary: expand the system type or the type of others
    • Defining aliases: simplest
    • Use combination: most commonly used (no seamless conversion can be made between defining alias and using combination, and attention should be paid to later maintenance)
    • Use inline: syntax sugar, save code. (poor readability)

9. Differences between extension and "inheritance" of other languages

  • Use inline

  • Rewriting in other languages is reflected in go. You can also define a method with the same name as TreeNode in myTreeNode. The prompt is: shadowed.

    • Directly calling the mynode. Method name will be calling the overridden method. However, the hidden mynode.TreeNode. Method name can call the original method.

    • There is a concept of polymorphism in java: the parent class and parent class reference point to the child class object (probably upper transformation):

      The parent class object is not accessible, and the child class method.

      The upper transformation object can access not only the parent method (called through super.), but also the member variables inherited or hidden by the subclass, or call the method inherited by the subclass (parent method) or the instance method overridden by the subclass. Furthermore, the reference of a parent type to an object of a subclass can not only use the powerful functions of the subclass, but also extract the commonness of the parent class.

      • Go language can't do this ~, to achieve similar functions, go language comes through interfaces.

Chapter 5: dependency management of go language (package management)

1. Basic concepts

  • Concept of dependency:
    • Dependence has many meanings
    • This refers to the extensive use of third-party libraries. We should build the functions on some infrastructure already implemented by others.
    • For example, pull a library of others on github, pull the code of the library to the code tree, and build our code with the library, so that it can work. This is the focus of our concern (dependence).
  • Dependency management has gone through three stages: GOPATH, vendor and go mod (mainstream).

2. GOPATH

  • This is the installation path of go

  • By default, ~ / go (UNIX, Linux),% userprofile% \ go (Windows) are all in the user directory

    • GOPATH is managed without. When specifying GOPATH, if you need anything, I will look under GOPATH.
    • Problem: all necessary or dependent libraries for the project are placed under the GOPATH directory. Will get bigger and bigger.
  • Practice GOPATH (remember to turn off GO111MOODULE first):

    • Turn off: the whole system changes: go env -w GO111MOODULE = "off", current window: export GO111MOODULE = "off"
    • GOPATH must also meet some directory conditions
    // Create a directory called gopathtest as GOPATH
    
    // directory structure
    gopathtest
    	src // This directory must have this name. Various codes can be placed under src
    
    // 1. go env -w GOPATH = "directory" changes the entire GOPATH of the system
    // 2. export GPPATH = "directory" temporarily changed. This terminal window only
    
    // Pull a project
    go get -u go.uber.org/zap  // Library name zap
    // When you see it again, it will appear in the src directory
    go.uber.org
    
  • Write code test: zaptest.go

     import "go.uber.org/zap"
    
      func main() {
          log, _ := zap.NewProduction()
          log.Warn("warning test")
      }
    

    • Generally, there are two places to find bags:
        1. GOROOT: there is a src under the location where go is installed. Find it below
        2. Go down to the specified GPPATH
  • Problem: for the zap Library in the above map, if two projects need to use this library, but the required versions are different, it can't be done

    • Solution: in the project directory, create a vendor directory and put the zap library below, which will give priority to the use here.
    // directory structure
    gopathtest
    	src
    		go.uber.org
    			zap  // zap Library 
    		project1   // Item 1
    			vendor  
    				go.uber.org // Put one on
    		project2   // Item 2
    			vendor 
    				go.uber.org // Put one on
    
    • You can't always get all dependencies and then manually copy them to the vendor, so there are some third-party dependency management tools
    • glide, dep, go dep, etc
    • The real operation is not to move the vendor directory, but to change the configuration files of various management tools. After the settings are clear, let the management tools complete the copy.

3. go mod usage

    1. Basic usage and introduction:
  • When you open GO111MOODULE, download this package again: go get -u go.uber.org/zap

    • You can specify the version go get -u go.uber.org/zap@v1.12.0
    • Then the code structure is as follows:
    gomodetest  // Create project name
    	go.mod  
    	go.sum  // For introduction, please refer to: https://my.oschina.net/renhc/blog/3171035 
    	zaptest.go  // Test code
    
    • go mod content:
    module gomodetest
    
    go 1.17
    
    require go.uber.org/zap v1.19.1
    
    require (
    	go.uber.org/atomic v1.7.0 // indirect
    	go.uber.org/multierr v1.6.0 // indirect
    )
    
    • zaptest code is the same as above
      • The version of the call is specified.
    • Zap is installed in go/pkg/mod: C:\Users\Administrator\go\pkg\mod\go.uber.org\zap@v1.19.1
    • command
      • go mod init + module name to generate mod file
      • go mode tidy to delete unused modules or add modules that are used but do not exist in go.mod
      • You can find it on the Internet. The current cognition can only take these notes on go mod, but the understanding is not deep enough.
    • If you want to change the version, you can directly modify go mod. For example, you can use version 1.12.0. After the modification, go mod tidy can be used. Open the corresponding directory and you will find both versions in it.
    • Two ways to generate good go mod. One is to go get... And the other is to run the program to help you get automatically according to imoprt
    1. Migrate old projects to go mod
      1. go mod init + module name (or others) to generate mod files.
      2. go build. /..., every time you encounter import, you will pull the required libraries and put them into a local unified cache.
    • If some management tools mentioned in GOPATH have been used in the project, mod may be generated according to their configuration information during init.

4. Summary go mod

  • It is managed by the go command. Users do not have to care about the directory structure. Unlike before (GOPATH), their projects (project1 and project2 must be placed in the GOPATH src directory). With vendors, it is more complex. Their projects must also have vendor directories.
  • Initialization: go mod init
  • Add dependency: go get, or write code directly. Dependency will be automatically pulled and added during build
  • Update dependency: go get, @v specify the version, go mod tidy remove redundant or add unnecessary.
  • Migration project: go mod init, go build. /

5. Catalogue arrangement

  • As mentioned before, a file directory can only have one package, and a package can only have one main function.
// A directory structure like this:
01
	arrays.go
	maps.go
	slices.go
	funcs.go

// 01 is a project I built. Each of the following is a file I created to learn the basic knowledge of each part.

When executed go build + Any file can be compiled, but when executed go build, Errors occur when all files are compiled at once:
main redeclaration in this block :  Obviously, there is one under each file mian Function is wrong.
  • There is no good solution. Sort each main function into different files, as shown in the following directory structure
// Put each file in a separate folder to have a separate main function.
01 
	arrays	
		arrays.go
	maps
		maps.go
	slices
		slices.go
	funcs
		func.go
  • Now you need to execute: go build. /... To compile. Build and compile the files in the current directory (empty) and all subdirectories.
  • This is indeed a little cumbersome, but it is like this ~, and so is the official toolkit tools of go language. A command, placed in a folder. More engineering and clearer code structure.
  • (I don't understand it at the moment) there will be compiled result files after compilation. Generally, half of the compilation fails in this directory. Compiled successfully and placed in the bin directory under GOPATH.
    • Compile multiple packages at a time and there will be no results. It can be generated by go install. /.

Chapter 6: Interface

// Take a look first, and then come back to this structure. Python also has this "duck type". It feels like go here is changing "static type" into "dynamic type".  
// This interface does not need to consider what Traversal refers to, and whether the interface is implemented (not in java. go) as long as the Traverse() method is satisfied. It can be it. 
// "It feels like you don't need to determine the type when defining variables, but dynamically determine it at run time ~," getTraversal() passes what type to meet the method defined by the interface, and the interface can be that type.

type Traversal interface {
    Traverse()
}

func main() {
    traversal := getTraversal()
    traversal.Traverse()
}

1. Outgoing interface

  • Realize the decoupling of main function and function function function. The main function only needs to call the function function, not to implement the function
Write a file: downloader.go

package main

import (
	"fmt"
	"io/ioutil"
	"net/http" 
)

// Define a function that requests a link
func retrieve(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	bytes, _ := ioutil.ReadAll(resp.Body)
	
	// To string output
	return string(bytes)
}

func main() {
	fmt.Println(retrieve("https://www.imooc.com"))
} 
  • The coupling has been reduced here, but the main function must call the retrieve() function, which increases the coupling.
  • (suppose you are writing a project) let's assume that the function of main() is completed by a department. retrieve is done by another department (infra).
// Current directory structure
downloader.go
infra  // Under the infra package, there is a method to parse the url
	urlretriever.go
  • urlretriever.go:

    package infra
    
    import (
    	"io/ioutil"
    	"net/http" 
    )
    
    // To introduce an interface, first define it as a structure
    type Retriever struct {}
    
    func (Retriever) Get(url string) string{  // You don't need to name the recipient because you can't use it
    	resp, err := http.Get(url)
    	if err != nil {
    		panic(err)
    	}
    
    	defer resp.Body.Close()
    	bytes, _ := ioutil.ReadAll(resp.Body)
    	
    	// To string output
    	return string(bytes)
    }
    
  • downloader.go :

    package main
    
    import (
    	"fmt"
    	"01/infra"     
    )
    
    func main() {
    	retriever := infra.Retriever{}   // Build an object and call it
    	fmt.Println(retriever.Get("https://www.imooc.com"))
    } 
    
  • A new problem: the phrase "retriever: = infra.Retriever {}" does not always call infra.Retriever {}. It does not belong to a logic, but more like a configuration. Extract it.

    dowloader.go It's written in Chinese
    
    func getRetriever() infra.Retriever {
    	return infra.Retriever{}
    }
    
    func main() {
    	retriever := getRetriever()  // Get the structure object through the method
    	fmt.Println(retriever.Get("https://www.imooc.com"))
    }
    
  • New problem: in this way, there is still a strong coupling, and the type of retriever cannot be changed. He has always been of type infra. Retriever. Retriever: = getRetriever() is always equivalent, so var retriever infra.Retriever = getRetriever() is of type infra.Retriever. If we have another team (testing), they also have a function to parse URLs. Then, it is very troublesome to call this testing.Retriever. You need to change the getRetriever method to testing.Retriever type from beginning to end

    // A new directory structure is added to the testing department
    downloader.go
    infra  // Under the infra package, there is a method to parse the url
    	urlretriever.go
    testing
    	retriever.go 
    
  • retriever.go:

    package main
    
    type Retriever struct{}
    
    func (Retriever) Get(url string) string {
    	return "fask content"    // The implementation content is different from that of infra department
    }
    
  • Then the final modification: (export the interface concept)

package main

import (
	// "01/infra"
	"fmt"
	"01/testing"
)

// The return value is an interface type and must have a Get method
func getRetriever() retriever {  // Return type retriever
	return testing.Retriever{}
}

// Define interface
type retriever interface {
	// Get
	Get(string) string
}

func main() {
	var r retriever = getRetriever()
	fmt.Println(r.Get("https://www.imooc.com"))
} 

// At this time, it is completely decoupled. We only need to change the return value in the getRetriever() method to infra.Retriever {}, and then we can call go in infar without moving anything else.
  • Interface is an abstract concept, no matter what it is. The above definition of it is that as long as it can call the GET method.
    • So you can assign it to testing.Retriever {} or infra.Retriever {}
    • If we define the method in the interface as GET1, an error will be reported when running again:
      • testing.Retriever does not implement retriever (missing Get1 method)
    • The interface in go is different from java. After learning java, you may wonder that the testing.Retriever {} object does not implement the interface? How can I use it? This is where the go language is special and advanced: duck typing.

2. duck typing

  • As I said before, go is not like an object-oriented language. It only supports encapsulation, such as inheritance and polymorphism. What needs to be done is done by using interfaces instead in go. Therefore, the interface in go language is much more flexible than that in other languages.

  • Duck type: simply put: "walk like a duck, bark like a duck (look like a duck), then he is considered a duck". Different people have different perceptions of things. Whether it is a "duck", from my point of view, to meet my needs, I think it is a "duck". In programming: as long as you can provide the capabilities (Methods) defined in my interface, I think you are a "duck". To meet my needs, I can assign values to your type and interact with these capabilities.

  • In addition, go language is a structured type system, similar to duck typing, because when duck typing was mentioned, it must be bound dynamically! (refer to Python interpretive language duck type), but go language is bound at compile time ~ it can only be similar. See here, you can understand the opening paragraph and code.

  • duck typing in Python

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-SG8CZ2og-1635157982895)(.\py_duck.png)]

  • **duck typing in C + + * *

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nND91X9P-1635157982896)(./c++_duck.png)]

  • java has no duck typing, only similar code

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-if6uPutl-1635157982898)(./java_duck.png)]

  • duck typing in go

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Ol3j3bmA-1635157982898)(./go_duck.png)]

###3. Definition and implementation of interface

  • The interface of go is user-defined.
    • In the previous example, the former is the user and the latter is the definer. download uses the Get method of the retriever. download defines an interface with a Get method.
    • The implementation of the interface is implicit. As long as the method defined in the interface is implemented, the type (Definer) even implements the interface
// directory structure
retriever
	mock 
		mockretriever.go  //Definer
	main.go    // User
  • main.go: Caller
package main

import (
	"01/retriever/mock"
	"fmt"
)

type Retriever interface {
	Get(url string) string
}

func download(r Retriever) string {
	return r.Get("www.imooc.com")
}

func main() {
	var r Retriever
	r = mock.Retriever{"this is me"}
	fmt.Println(download(r))
}
  • mockretriever.go
package mock

type Retriever struct{
	Contents string
}

// Implement the Get method. (because of the user, the definer implements the Get interface by default)
func (r Retriever) Get(url string) string {
	return r.Contents
}

###4. Value type of interface

  • According to the above example, r represents the value type. In other languages, it may be just a reference or a pointer. But all in the go language are value types. r doesn't have a pointer inside. It's so simple. There are other things.
var r Retriever
fmt.Printf("%T, %v\n", r, r)
r = mock.Retriever{"this is me"}
fmt.Printf("%T, %v\n", r, r)

// Print results
<nil>, <nil>
mock.Retriever, {this is me}  // r has a type and a value inside


// Change function Get to receive pointer
func (r *Retriever) Get(url string) string {
	return r.Contents
}

var r Retriever
fmt.Printf("%T, %v\n", r, r)
r = &mock.Retriever{"this is me"}  // Get address
fmt.Printf("%T, %v\n", r, r)

// Print results
<nil>, <nil>
*mock.Retriever, &{this is me}   // All pointers


Value passing is mock.Retriever{"this is me"} An object is generated and copied to r In the stomach.
Pointer passing is to put the pointer of an object into r In the stomach.
  • View type of r

    • Method 1
    // switch can get the type of r
    	switch v := r.(type) {
    	case *mock.Retriever:
    		fmt.Println(v.Contents)
    	}
    
    
    • Method 2: Type assertion
    // Type assertion
    	mockRetriever, ok := r.(*mock.Retriever)  // When it is a value, it is r.(mock.Retriever)
    	// ok is used to determine whether the type is correct
    	fmt.Println(ok, mockRetriever.Contents)
    
  • Interface variables (such as r)

    • There is the type of implementer, the value of implementer (or the pointer of implementer, pointing to an implementer)
    • Interface variable with pointer
    • Interface variables are also passed by value, and there is almost no need to use the pointer of the interface (because there is a pointer in the belly of the interface variable).
    • The pointer receiver implementation can only be used in pointer mode; Value recipients can
  • Any type: interface {}

    q := []interface{} {1, "ahah", '1'}
    
    // q becomes, like a list in Python. Can receive any type.
    

5. Combination of interfaces

  • First:
// Combine two existing interfaces
type RetrieverPoster interface {
    Retriever  // Implement Get
    Poster     // Implement Post
}

s := &mock.Retriever{"this is me"}  // Define another Post method in mock.Retriever

func session(s RetrieverPoster){
    s.Get()
    s.Post()
}
  • Second:
// Direct definition interface
type Rf interface { 
	Post(url string) string
	Get()
}
  • For example, the io.go file on the official website provides many combined interfaces: readable, writable, readable and writable, etc.
// Partial code
type Seeker interface {
	Seek(offset int64, whence int) (int64, error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

6. Common system interfaces

  • stringer: equivalent to tostring in java

    fmt/print.go There is one below stringer. 
    
    type Stringer interface {
        String() string
    } 
    
    // As long as our own object implements the method (String ()), we can implement the interface.
    fmt.Println(object)   // Will be called.
    
  • Reader

    io/io.go There is one below Reader, Writer
    
    type Reader interface {   // Realize the function of reading files
        Read(p []byte) (n int, err error)
    }
    
    type Writer interface {   // Realize the function of writing files: byte, slice and string can be used as readers and writers
        Write(p []byte) (n int, err error)
    }
    
     Abstraction, any implementation and  Read,  Write The objects of the method implement these two interfaces and can be read and written.
    

  • Writer

Chapter 7: functions and closures (functional programming)

###1. Basic concepts

  • Functional programming VS function pointer
    • Functions are first-class citizens: parameters, variables, and return values can all be functions
    • In c + +, there are only function pointers. In java, functions are just names. There is no way to pass names to others
    • Functions can be passed as parameters in python.
  • "Orthodox" functional programming
    • Immutability, no state, only constants and functions. Neither selection statement nor loop statement can be used
    • Function has only one argument
  • go and python are not really functional programming ~ the usage is more flexible
package main

import "fmt"

// Realize accumulation
func adder() func(int) int {
	sum := 0
	var a func(i int) int = func (i int) int {
		sum += i
		return sum
	}
	return a
}

func main() {
	a := adder()
	for i := 0; i < 10; i++ {
		fmt.Println(a(i))
	}
}


// The function inside can only be written like this
1. var a func(i int) int = func (i int) int , Define a variable to receive
2. More concise use of anonymous functions, save the name
func adder() func(int) int {
	sum := 0
	return func (i int) int {
		sum += i
		return sum
	}
}
  • The above example demonstrates the concept of closure (related concepts have been learned in Python)

    • The inside of a function is called a local variable
    • If it is outside function a but inside function adder, and function a is also defined inside function adder, sum is called a free variable. (closure namespace in Python)
  • Closure in Python

    [external chain picture transfer failed. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-hLAivqUt-1635157982899)(./py_Closure.png)]

  • Closures in c + + (relatively new syntax, after c++ 14)

    [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Gqe5ou6e-1635157982900)(./c++_Closure.png.png)]

  • The definition in java should be stricter: use the final keyword to define a class. holder, there is only one value in it

  • Functions in java are not first-class citizens and do not support functions as parameters and return values, so an object is used to simulate a function.

    [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-tvLjX2Oz-1635157982901)(./java_Closure.png)]

Example 1: Fibonacci

  • Realize fibonacci
package main

import "fmt"

func fibonacci() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}

Example 2: implementing interfaces for functions (continued fibonacci).

  • Similar to file printing (io.Reader in the system interface mentioned above), implement the printing interface for fibonacci and print the number of fibonacci. (it's difficult. I've understood it for a long time. I have to watch the video carefully for many times, look back again and again, and understand it in series. It involves a lot of knowledge)
package main

import (
	"bufio"
	"fmt"
	"io"
	"strings"
)

func fibonacci() intGen {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}

type intGen func() int  // Function type is a flexible place where a type can implement interface ~~ go language
	
// It's actually a kind of grammar sugar, as mentioned earlier. 
// (g intGen), which is the g.Read() call. If you don't write this, it is a Read(g) call. No, very special, just ordinary functions
// Therefore, even function objects can implement interfaces (implementing methods, which are used by users, is equal to implementing interfaces)
func (g intGen) Read(p []byte) (n int, err error) {
	// n: How many bytes are written? error exception
	// Next calls g to read the next Fibonacci number
	next := g()
	// Control greater than 10000 stops
	if next > 10000 {
		return 0, io.EOF
	}
	// Call Sprintf, calculate the corresponding number of bytes, convert it to string, add line feed, and save it in p
	s := fmt.Sprintf("%d\n", next)
	// Proxy write p operation through NewReader
	return strings.NewReader(s).Read(p)   // strings.NewReader(s) returns a Reader object. It calls the Read() method to write s to p
}


func printFileContents(reader io.Reader) {
	scanner := bufio.NewScanner(reader)    // Click the source code to find that io.Reader is packaged as a Scanner object

	for scanner.Scan() {  // Loop judgment, Scan() Text() are the methods provided by the Scanner object for output
		fmt.Println(scanner.Text())
	}
}


func main() {
	f := fibonacci()
	//  The printFileContents function defines the parameter reader as the io.Reader interface type. So you need to f implement the reader method
	// Then, the printFileContents function continuously calls the Read method to write data and then Read.
	// And Read internally implements a function of adding a Fibonacci number to p each time.
	printFileContents(f)

	// The overall process is as follows: 1. Pass the f object as a parameter to the printFileContents() function
	// 2. The io.Reader interface type variable is used internally to receive F, and f implements the reader method
	// 3. Continuously call next: = go() internally to write the next Fibonacci number for printing.


	// Difficulties. 1. Understanding of the implementation of the io.Reader interface; 2. Implementing the Reade method for a function type (indirectly, implementing the io.Reader interface)
}

Example 3: use function to traverse binary tree

package main

import "fmt"

// Define node
type TreeNode struct {
	left, right *TreeNode
	val int
}

// Medium order traversal
func (node *TreeNode) TraverseFunc(f func(*TreeNode)){  // Pass a function
	if node == nil {
		return 
	}

	node.left.TraverseFunc(f)
	f(node)
	node.right.TraverseFunc(f)
}


func main() {
    root1 := TreeNode{val: 3}
    
    // Cumulative counting of nodeCnt
	nodeCnt := 0
    // Pass anonymous functions to realize cumulative counting, and count the number of nodes in order
	root1.TraverseFunc(func (node * TreeNode){
		nodeCnt ++
	})
	fmt.Println(nodeCnt)
}
  • The closure of go language is more natural (Python has closure space, so it is not clear about the variable management of go. Python needs to use nonlocal declaration, but go does not)
  • go does not have an anonymous Lambda expression, but has an anonymous function. Anonymous function is enough. It is required to be concise. There is no need to add syntax to realize Lambda expression.

Chapter 8: resource management and error handling

  • resource management
    • For example, opening a file requires closing it
    • To connect to the database, you need to release
    • They all appear in pairs, but as soon as they are mixed with error handling, they become more complex. If the program is abnormal in the middle, how to ensure that the open link is closed? This is resource management and error handling

1. defer call

  • Ensure that the call occurs at the end of the function

  • Defer list and meet the requirements of first in and last out (that is, the order in which defer statements appear, first appear and then execute)

  • Parameters are evaluated at the time of a defer statement

    func tryDefer() {
    	defer fmt.Println(1)   // Output 2 first, then output 1
    	fmt.Println(2)
    }
    
    func tryDefer() {
    	defer fmt.Println(1)
    	defer fmt.Println(2)
    	fmt.Println(3)
    }
    // Ensure first in and last out. So the print result is 3 2 1
    
    func tryDefer() {
    	defer fmt.Println(1)
    	defer fmt.Println(2)
    	fmt.Println(3)
    	return // panic("error") this does not affect, and the output result is still 3 2 1. Even if the following program is interrupted, it still does not affect the expected execution
    	fmt.Println(4)
    }
    
  • Write a file in practice (create a file and write fibonacci sequence)

    • Resource management of go language: create a file -- > close the file. Write cache -- > flush() appears in pairs, so we can directly defer close() after opening the file
    package main
    
    import (
    	"01/functional/fib"
    	"bufio"
    	"fmt"
    	"os"
    )
    
    func writeFile(filename string) {
    	file, err := os.Create(filename)
    	if err != nil {
    		panic(err)
    	}
    
    	defer file.Close() // Close file on completion
    	// Writing files directly is slow, using bufio
    	writer := bufio.NewWriter(file)   // The buffer buffer is written to the memory first, and when it reaches a certain size, it is written to the hard disk at one time
    	defer writer.Flush() // Only the buffer buffer is written and needs to be imported
     
    	f := fib.Fibonacci()
    	for i := 0; i < 20; i++ {
    		fmt.Fprintln(writer, f())
    	}
    
    }
    
    func main() {
    	writeFile("fib.txt")
    }
    
  • Parameters are evaluated during the defer statement:

    func forDefer() {
    	for i := 0; i < 100; i++ {
    		defer fmt.Print(i, " ")
    		if i == 30 {
    			panic("error")
    		}
    	}
    }
    
    The print result is:
    // 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 panic: error
    
    // Output 30 first, and then output 29.. 28.. 0 to meet the requirements of first in first out. Moreover, instead of printing 30 30. Description i parameter values, they have been calculated when the program executes the defer statement.
    
  • A super interesting example:

package main

import "fmt"

func f1() int {
	x := 5
	defer func() {
		x += 1
	}()
	return x
}

func f2() (x int){
	defer func() {
		x++
	}()
	return 5
}


// The output is 5, 6   
  • The analysis doesn't understand, but according to the first example: pass a pointer instead
func f1() *int {
	x := 5
	defer func() {
		x += 1
	}()
	return &x
}

a := f1()
fmt.Println(*a)


// The result is 6. It can only be said to be value transfer. return returns the value 5. x, right after it++
  • Brief analysis: defer is indeed the second one executed after return. Because you defined x when you return, you can add it. The first one returns a value of 5.

2. Error handling

  • In fact, the error handling of go language is mainly around error. For example, file, err: = OS. Create (filename).

    • Judge this err. Do some wrong handling.
    • For example, file reading and writing.
    func writeFile(filename string) {
    	file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) // 0666 linux file permissions, added this os.O_EXCL, the file cannot be opened if it exists. Artificially create an error exception
    	if err != nil {
    		// panic(err) you can't simply panic and interrupt the program here. Detailed error handling can be carried out
    
    		// 1. Print out and return. Do not let exceptions interrupt the program
    		// fmt.Println("file already exists ", err)
    		// return 
    
    		// 2. Open the source code of OpenFile() function and you will find that if there is an error, it will be of type * PathError. Err is the interface type and implements the Error() method. If an error is reported here, it will return * PathError
    		if pathError, ok := err.(*os.PathError); !ok {  // Type assertion to see if an error of this type will be reported
    			panic(err)  // When the type of * os.PathError is not sent in, panic is sent directly
    		} else {
    			fmt.Println(pathError.Op,  // Output some information about pathError
    			pathError.Path,
    			pathError.Err)
    		}
    		return
    	}
        
        
    // Print result: open fib.txt The file exists. Corresponds to the above three prints.
    
    • You can also customize Error: err = errors.New("this is a custom error"), or define an Error method to implement the interface.

3. Server unified error handling

  • How to implement unified error handling logic

    • Implement a webserver that displays files and access the fib.txt file generated above
    • File directory
    errhandling
    	defer
    		fib.txt  // fib file
    	filelisting
    		web.go   // webserver, accessing fib files
    
  • Initial version (implementation logic)

    // web.go 
    
    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"os"
    )
    
    // Implement a web server that displays files
    
    func main() {
    	http.HandleFunc("/list/", 
    	func (writer http.ResponseWriter,
    		request *http.Request) {
    		// Get the specific file name or directory behind the list
    		fileName := request.URL.Path[len("/list/"):]  // /list/fib.txt, through slicing, as long as the back. fib.txt
    		// Open file
    		fibPath := fmt.Sprintf("%s%s", "../defer/", fileName)  // Get the file name and splice the path
    		file, err := os.Open(fibPath)
    		if err != nil {
    			panic(err)
    		}
    		defer file.Close()
    		// read file
    		all, err := ioutil.ReadAll(file)
    		if err != nil {
    			panic(err)
    		}
    		// Write the content to the ResponseWriter
    		writer.Write(all)
    	})
    
    	err := http.ListenAndServe(":8888", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • You can try to access: http://localhost:8888/list/fib.txta , you will get an error message
      • http: panic serving 127.0.0.1:60714: open .../defer/fib.txta: The system cannot find the file specified.
      • The os.Open function will report an error and panic interrupts the program, but it is not completely interrupted. A bunch of error prompts are printed, and then if you enter the correct url at this time, you will get the correct prompt. The server does not stop (generally, panic interrupts the whole program, but in HTTP server, panic does not interrupt the program)
  • Attempt to process error:

    • Remove the panic for treatment
    file, err := os.Open(fibPath)
    		if err != nil {
    			http.Error(writer,  
    			err.Error(),
    			http.StatusInternalServerError)  // code
    			return
    		}
    
    // Finally, execute the wrong url, and the result will be displayed in the browser without printing a bunch of error messages in the background
    // Open.. / defer / fib.txta: the system cannot find the file specified
    
    • Disadvantages: this will display the internal error information to the user.

  • Wrap these error messages into an external err.

    • Look at the new directory structure:
    filelisting
    	listing
    		handeler.go  // Extract the logic of function processing
    	web.go  // mian letter
    
    • Take out the anonymous processing function required by http.HandleFunc(), put it in a separate file (handler. Go), and only implement the main logic, return all errors and hand them over to the outside for processing.
    • handeler.go: handle logic normally, and directly return an error when encountering a problem.
    package listing
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"os"
    )
    
    func HandFileList(writer http.ResponseWriter,
    	request *http.Request) error {
    	// Get the specific file name or directory behind the list
    	fileName := request.URL.Path[len("/list/"):]  // /list/fib.txt, through slicing, as long as the back. fib.txt
    	// Open file
    	fibPath := fmt.Sprintf("%s%s", "../defer/", fileName)
    	file, err := os.Open(fibPath)
    	if err != nil {
    		return err
    	}
    	defer file.Close()
    	// read file
    	all, err := ioutil.ReadAll(file)
    	if err != nil {
    		return err
    	}
    	// Write the content to the ResponseWriter
    	writer.Write(all)
    	
    	return nil
    }
    
    • web.go: define a function type appHandler.
    • Define an errWrapper() function. Pass the processing function as a parameter and handle internal errors uniformly.
    • It seems very complex, but it is actually unfamiliar with the semantics of go language. In vernacular, handeler.go is a function that defines normal processing logic. Then we define a function. The return value of this function is a function. The internal call of the function completes the business processing function of handler.go. The returned exception is accepted. The following exceptions are handled and judged.
    package main
    
    import (
    	"01/errhandling/filelisting/listing"
    	"net/http"
    	"os"
    )
    
    // Define a function type and return error
    type appHandler func(writer http.ResponseWriter,
    	request *http.Request) error
    
    // Define another function to return the function required by the HandleFunc() parameter
    func errWrapper(
    	handler appHandler) func(
    		http.ResponseWriter, *http.Request) {
    	return func(writer http.ResponseWriter, 
    	request *http.Request) {
    		err := handler(writer, request)
    		if err != nil {
    			code := http.StatusOK
    			switch {
    			// If the file does not exist
    			case os.IsNotExist(err):
    				http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    				code = http.StatusNotFound
    			// No authority
    			case os.IsPermission(err):
    				code = http.StatusForbidden
    			// default
    			default:
    				code = http.StatusInternalServerError
    			}
    
    			http.Error(writer,
    			http.StatusText(code), code)
    		}   
    	}
    }
    

    //Implement a web server that displays files
    func main() {
    http.HandleFunc("/list/", errWrapper(listing.HandFileList)) / / process logic functions and abstract them
    err := http.ListenAndServe(":8888", nil)
    if err != nil {
    panic(err)
    }
    }

4. panic and recover

  • panic

    • Stop the execution of the current function (I think it's a bit like raise in Python)
    • Always go back up and execute the defer of each layer
    • If recover is not encountered, the program exits
    • Panic (English: panic, hate). That is, when the program encounters problems, it is very panic, very angry and doesn't know what to do. So panic is a very heavy word. Generally speaking, try to use as little as possible.
  • recover

    • Used only in defer calls
    • You can get the value of panic
    • If it cannot be processed, the panic can be reset
    package main
    
    import (
    	"fmt"
    )
    
    
    func tryRecover() {
    	defer func() {
    		r := recover()  // func recover() interface{} .  You can see that recover () is of any type
    		if err, ok := r.(error); ok {
    			fmt.Println("Error occurred: ", err)
    		} else {
    			panic(r)
    		}
    	}()  // Define an anonymous function and call
    
    	b := 0
    	a := 5 / b
    	fmt.Println(a)
    }
    
    func main() {
    	tryRecover()
    }
    
    
    
    // result
    Error occurred:  runtime error: integer divide by zero  // The program does not terminate abnormally
    
  • If the direct panic(123) is not an exception type, the panic is also sent out 123

    func tryRecover() {
    	defer func() {
    		r := recover()  // func recover() interface{} .  You can see that recover () is of any type
    		if err, ok := r.(error); ok {
    			fmt.Println("Error occurred: ", err)
    		} else {
    			panic(r)
    		}
    	}()  // Define an anonymous function and call
    
    	// b := 0
    	// a := 5 / b
    	// fmt.Println(a)
    	panic(123)
    }
    
    // The value of r is 123
    

5. Server unified error handling 2

  • After learning the above panic and recover, let's take a look again

    • If the person who writes the function processing logic above is not the same as the main function, it is like this to join the main
    http.HandleFunc("/", errWrapper(listing.HandFileList))  // Change the original / list / to/
    
    • Then the handler function in handeler.go is still processed as / list /
    fileName := request.URL.Path[len("/list/"):]
    
    • When input http://localhost:8888/abc When, an error will be reported, and the terminal will output a lot of error information! And it shows that the web page cannot be accessed normally ~ (this is not what we want) because the slicing operation exceeds the index length. Display some error information
    2021/10/07 17:17:34 http: panic serving [::1]:65487: runtime error: slice bounds out of range [6:4]
    goroutine 6 [running]:
    net/http.(*conn).serve.func1(0xc00005ab40)
            D:/software/go/go1.15/src/net/http/server.go:1801 +0x147
    panic(0x66a120, 0xc0000aa080)
            D:/software/go/go1.15/src/runtime/panic.go:975 +0x3e9
    01/errhandling/filelisting/listing.HandFileList(0x6d9200, 0xc0000ae0e0, 0xc0000a8000, 0x0, 0x0)
    
    • At this time, the server did not hang up. When the correct address is entered, it can still be accessed successfully. This is the * * protection * * of httpserver. At D:/software/go/go1.15/src/net/http/server.go:1801 +0x147 here:
    func (c *conn) serve(ctx context.Context) {
    	c.remoteAddr = c.rwc.RemoteAddr().String()
    	ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
    	defer func() {
    		if err := recover(); err != nil && err != ErrAbortHandler {
    			const size = 64 << 10
    			buf := make([]byte, size)
    			buf = buf[:runtime.Stack(buf, false)]
    			c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
    		}
    		if !c.hijacked() {
    			c.close()
    			c.setState(c.rwc, StateClosed)
    		}
    	}()
    
    • You can see that a recover () is performed, err: = recover (), and then some processing is performed.
  • Then our code func errWrapper also needs to add a defer, recover. Operate when the program terminates abnormally.

    func errWrapper(
    	handler appHandler) func(
    		http.ResponseWriter, *http.Request) {
    	return func(writer http.ResponseWriter, 
    	request *http.Request) {
            // !!! Add defer to handle exceptions
    		defer func() {
    			if r != nil {
    				r := recover()
    				log.Printf("Panic: %v", r)
    				http.Error(writer,
    					http.StatusText(http.StatusInternalServerError), // Return server error status code
    					http.StatusInternalServerError)
    			}
    		}()
    		err := handler(writer, request)
    		if err != nil {
    			code := http.StatusOK
    			switch {
    			// If the file does not exist
    			case os.IsNotExist(err):
    				http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    				code = http.StatusNotFound
    			// No authority
    			case os.IsPermission(err):
    				code = http.StatusForbidden
    			// default
    			default:
    				code = http.StatusInternalServerError
    			}
    
    			http.Error(writer,
    			http.StatusText(code), code)
    		}   
    	}
    }
    
    • Then we'll visit again http://localhost:8888/abc When, the browser will display: Internal Server Error. The background terminal can only print: 2021/10/07 17:38:51 Panic: runtime error: slice bounds out of range [6:4]
  • There are also main contents in the post sequence. I don't understand them very much. I'm beyond the foundation. I don't understand these methods. Give up first.

Chapter 9: testing

  • Skip first

Chapter 10: Goroutine

  • goroutine

    • Lightweight "threads"
    • Non preemptive multitask processing, and the cooperative process takes the initiative to hand over the control
    • Multitasking at compiler / interpreter / virtual machine level
    • Multiple coroutines may run on one or more threads
  • goroutine model:

    • goroutine does not correspond to physical threads one by one. It is a lot of map s to a physical thread. It will be mentioned below that even if 1000 goroutines are opened, the corresponding threads will only be opened according to the number of cores of the physical machine. Because physical threads are managed by the operating system. The overhead is relatively large and uncontrolled. Physical threads are preemptive multitasking.

[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nkzj62Vm-1635157982902)(./goroutine.png)]

  • First sight

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	for i := 0; i < 10; i++ {
    		go func(i int) {
    			for {
    				fmt.Printf("Hello from goroutine %d\n", i)  // Printing is an io operation. This sentence will block and hand over control
    			}
    		}(i)
    	}
    	// Sleep for 10 seconds. The main program and the open collaboration are executed together. When the main program is executed and the coroutine is not executed, the main() function ends and the whole program ends
    	time.Sleep(time.Millisecond*100)
    }
    
    • go keyword starts a Coroutine to execute a task by default
    • In learning python, we have implemented the contents of relevant collaborative processes:
      • 1. Generator coprocess scheduler: encapsulate the generator into a coprocess (through the decorator), and schedule the execution of tasks through the coprocess scheduler
      • 2. Collaboration scheduler, epoll: event driven tcp network server, collaboration scheduler. The socket is encapsulated as a co process adapter to realize the co process scheduler (eventloop), event driven programming is applied in the scheduler, and the co process tcp network server is realized.
  • Coroutine:

    • lightweight thread
    • For non preemptive multitasking, the cooperative process takes the initiative to hand over control. (decide for yourself)
    • Multitasking at the compiler (go) / interpreter / virtual machine level. (go compiler level has its own scheduler, which does not depend on operating system scheduling)
    • Multiple coroutines may run on one or more threads.
  • Change the above program to an array (if there is no operation such as io, there will be no switching and give up control)

    package main
    
    import (
    	"fmt"
    	"runtime"
    	"time"
    )
    
    func main() {
    	var a [10]int
    	for i := 0; i < 10; i++ {
    		go func() {
    			for {
    				a[i]++
    				runtime.Gosched()
    			}
    		}()
    	}
    	// Sleep for 10 seconds. The main program and the open collaboration are executed together. When the main program is executed and the coroutine is not executed, the main() function ends and the whole program ends
    	time.Sleep(time.Millisecond)
    	fmt.Println(a)
    }
    

  • Two data conflicts: use go run -race goroutine.go to view data conflicts

    • The execution of the main function is also a coroutine. The inner function is equal to using the outer closure variable i. It is likely that i in main will not be used in the function until i reaches 10, which will exceed the maximum length of the array. So pass i inside the anonymous function.
    • Finally, main prints the array, but other coroutines may still write data to the array ~ this is another conflict, which needs to be handled by channel.
  • Any function can be sent to the scheduler for execution by adding go

    • Distinguish whether it is an asynchronous function when it does not need to be redefined (async def for Python)
    • The scheduler will switch at the appropriate point
    • Use - race to detect data access conflicts
  • goroutine possible switching points (non preemptive)

    • I/O, select
    • channel
    • Waiting lock
    • Function call (sometimes)
    • runtime.Gosched()
    • The above is only for reference, and there is no guarantee that it will not be switched in other places
  • If 1000 coprocesses are opened continuously, the go language will open about seven threads, and the scheduler will only have a corresponding part of the threads active according to the number of cores of your machine. (top command can be viewed in linux system)

Chapter 11: Channel

1. channel bidirectional channel

  • Channel: bidirectional channel between goroutine and goroutine.

    • Creation method:
    var c chan int // c == nil
    a := make(chan int)  // It can be written as a: = make (Chan, int, 3) plus cache and specify space
    
    • channel is the same as a function. It is a first-class citizen and can be used as a function parameter and return value.
    • var channels [10]chan int, which defines an array. Each element is a channel
    • In addition, as a function return value
    func createWorker(id int) chan int {
    	// Create channel in function
    	c := make(chan int)
    	// You must create a coroutine to handle the channel. If you don't define the for loop into func, the program will be dead loop! Waiting to receive data.
    	go func() {
    		for {
    			fmt.Println(<- c)
    		}
    	}()
    
    	// Return to channel
    	return c
    }
    
    // The createWorker function is used to create and return a channel. He didn't do anything. The real processing is in the internal func.
    
    // It can also specify the direction, and the external person can only receive the arrow sign
    func worker(id int) chan <- int {}
    
  • C: = make (Chan, int, 3) specifies the size and creates a buffer, which is helpful for performance. Three values can be imported cumulatively before switching and being received by other processes.

  • You can also stop sending: close(a) a is channel. This is the use of close() for sending and sending. The receiver can also receive data, but all received are 0 (a chan int).

    • The receiver can judge whether the sender has called close() to receive data. Use n, ok: = < - C (c chan int). Use ok to judge whether it is close()
    • In a more efficient way, you can use the range loop directly.
    for n := range a {    // range will automatically determine whether the sender calls close()
    		fmt.Println(n)
    	}
    
  • Theoretical basis of channel: Communication Sequential Process (CSP)

  • Don't communicate by sharing memory; share memory by communicating.

    • Do not communicate through shared memory; Share memory through communication.

2. Use channel to wait for the task to end

  • In the previous example, in the main program, time.Sleep() is used to wait. It is written as a task outside the mian function for execution. This method is not very good. We need a professional way to wait for the end of the channel task, and then end the main() function.

    • In the above example, we can send two channel s, one sending task and one receiving task.
    // Define channel
    var in chan int
    var done chan bool
    
    // 1. Sender
    in <- 1
    in <- 2
    <- done
    <- done
    
    // recipient
    var n int
    n <- in
    n <- in
    
    done <- true
    done <- true
    

    In this way, Sleep() does not need to be added, because after the sender sends in, it also needs to receive the data sent by the receiver done (no actual reception), which leads to the need to wait for the receiver to send data, so that the task of the receiver in waiting for the task is over!

    * With a theoretical basis, we can write small examples and put them into practice.
    
    ```go
    package main
    
    import (
    	"fmt"
    )
    
    func doWork(id int,
    	in chan int, done chan bool) {
    	// range read in channel data
    	for n := range in {
    		// Processing in received data
    		fmt.Printf("Worker %d received %c\n",
    			id, n)
    		// The feedback information has been processed
    		done <- true
    	}
    }
    
    type worker struct {
    	 in chan int
    	 done chan bool
    }
    
    
    // Create work
    func createWorker(id int) worker {
    	w := worker {
    		in: make(chan int),
    		done: make(chan bool),
    	}
    	go doWork(id, w.in, w.done)
    	return w
    }
    
    // example
    func chanDemo() {
    	// Create ten workers
    	var workers [10]worker
    	for i := 0; i < 10; i++ {
    		workers[i] = createWorker(i)
    	}
    
    	// Assign tasks to each worker and pass in a value
    	for i, worker := range workers {
    		worker.in <- 'A' + i
    	}
    
    	// Assign tasks to each worker and pass in a value
    	for i, worker := range workers {
    		worker.in <- 'a' + i
    	}
    
    	// wait for all oh them, wait for the return of all done, so that the sender can execute it concurrently
        // Because each doWork above receives values twice, it needs to receive values twice
    	for _, worker := range workers {
    		<- worker.done
    		<- worker.done
    	}
    }
    
    func main() {
    	chanDemo()
    }
    
    
    // Writing like this will not execute correctly!!, The reason is that after a data is processed in dowork, it will be < - done, but it is not processed in chanDemo. Even if this is processed, dowork will always be stuck in < - done, waiting for others to receive it, but chanDome does not receive it, but sends a group of data to in < -. This leads to both sides waiting and deadlock.
    
    The solution can be 
    1. doWork Medium:  go func() {done <- true}  // Create a process again and execute this sentence.
    
    2. Or: set cache!
    createWorker() In function:
    w := worker {
    		in: make(chan int),
    		done: make(chan bool),
    	}
    
    You can set the cache when done <- true When, they are immediately waiting for others to collect
    w := worker {
    		in: make(chan int),
    		done: make(chan bool, 2),  // Can cache
    	}
    
    3. Or the actual business will not have too complex concurrency. Here, you can separate the reception. Do not wait for the results of two batches of tasks at one time
    
    chanDome() Medium:
    	// Assign tasks to each worker and pass in a value
    	for i, worker := range workers {
    		worker.in <- 'A' + i
    	}
    
    	// After sending a group, wait for the result immediately
    	for _, worker := range workers {
    		<- worker.done
    	}
    
    	// Assign tasks to each worker and pass in a value
    	for i, worker := range workers {
    		worker.in <- 'a' + i
    	}
    	
    	// After sending a group, wait for the result immediately
    	for _, worker := range workers {
    		<- worker.done
    	}
    

  • Wait using sync.WaitGroup

    var wg sync.WaitGroup
    // Three methods are provided
    wg.Add()  // Receiving int type: the number of tasks is used by the receiver
    wg.Done()  // The end of task processing flag is used by the processor
    wg.Wait()  // Waiting for all tasks to end is used by the processor
    
  • Remove the done channel of the above code and use WaitGroup instead

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    func doWork(id int,
    	in chan int, wg *sync.WaitGroup) {
    	// range read in channel data
    	for n := range in {
    		// Processing in received data
    		fmt.Printf("Worker %d received %c\n",
    			id, n)
    		// The feedback information has been processed
    		wg.Done()
    	}
    }
    
    type worker struct {
    	 in chan int
    	 // Add WaitGroup
    	 wg *sync.WaitGroup  // Use the pointer to call the same copy. It can't be alone
    }
    

    //Create work
    func createWorker(id int, wg *sync.WaitGroup) worker {
    w := worker {
    in: make(chan int),
    wg: wg,
    }
    go doWork(id, w.in, w.wg)
    return w
    }

    //Examples
    func chanDemo() {
    //Create WaitGroup
    var wg sync.WaitGroup
    //Create ten workers
    var workers [10]worker
    for i := 0; i < 10; i++ {
    workers[i] = createWorker(i, &wg)
    }

    //We set a total of 20 tasks below
    wg.Add(20)

    //Assign tasks to each worker and pass in a value
    for i, worker := range workers {
    worker.in <- 'A' + i
    //In addition to the one-time Add(20), when the total number of tasks is not known, you can create an Add(1) here
    }

    //Assign tasks to each worker and pass in a value
    for i, worker := range workers {
    worker.in <- 'a' + i
    }

    //Wait
    wg.Wait()
    }

    func main() {
    chanDemo()
    }

3. channel traverses the tree and compares it with functional recursion

  • Upper code

    package main
    
    import "fmt"
    
    type TreeNode struct {
    	Left *TreeNode
    	Right *TreeNode
    	Val int
    }
    
    //1. Use functional programming to traverse the tree and middle order
    func (node *TreeNode) TraverseFunc(f func(*TreeNode)) {
    	if node == nil {
    		return
    	}
    
    	node.Left.TraverseFunc(f)
    	f(node)
    	node.Right.TraverseFunc(f)
    }
    
    // 2. Use channel to traverse the number
    // As in the previous example, TraverseWithChannel() is equivalent to createWorker. Create a channel and return
    func (node *TreeNode) TraverseWithChannel() chan *TreeNode {
    	out := make(chan *TreeNode)
    	// Open goroutine and write node
    	go func() {
    		node.TraverseFunc(func(node *TreeNode) {
    			out <- node
    		})
    		// close
    		close(out)
    	}()
    
    	return out
    }
    
    
    func main() {
    	// 0. Create the tree first
    	root := TreeNode{nil, nil, 1}
    	root.Left = &TreeNode{nil, nil, 5}
    	root.Right = &TreeNode{Val: 4}
    	root.Right.Left = new(TreeNode)
    	root.Left.Right = &TreeNode{nil, nil, 3}
    
    
    	// 1. Call functional traversal, f anonymous function, and count the number of nodes
    	count := 0
    	root.TraverseFunc(func(node *TreeNode) {
    		count++
    		fmt.Println(node.Val)
    	})
    	fmt.Println("Tree has Node count: ", count)
    
    
    	// 2. Through channel
    	c:= root.TraverseWithChannel()
    	// The nodes can be traversed through the range, and the largest element value can be counted
    	maxNodeVal := 0
    	for node := range c {
    		if node.Val > maxNodeVal {
    			maxNodeVal = node.Val
    		}
    	}
    	fmt.Println("Max Node Value: ", maxNodeVal)
    }
    
    
    // Here some are like yield in Python. Out < - node, then switch. After you get it in the range, execute the switch again.
    // The channel of go language can do more complex things, but there are some functions equivalent to yield
    

4. select to schedule

  • The example given by the teacher is quite complex. It also talked about the timer, which is a very useful scenario example. I just listened to the back.

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    // Create channel
    func generator() chan int {
    	out := make(chan int)
    	i := 0
    	go func() {
    		for {
    			// Random sleep 1500 ms
    			time.Sleep(
    				time.Duration(rand.Intn(1500)) *
    					time.Millisecond)
    			out <- i
    			i++
    		}
    	}()
    	return out
    }
    

    func main() {
    var c1, c2 = generator(), generator()
    //If we want to receive data from c1 and c2, and who will receive it quickly, we need to use select
    //n1 := <- c1
    //n2 := <- c2

    //It's like doing a non blocking process ~ return the result immediately
    for {
    select {
    case n := <- c1:
    fmt.Println("Receive from c1: ", n)
    case n := <- c2:
    fmt.Println("Receive from c2: ", n)
    //Default: / / if you do not add default, you will get blocked and wait for data
    // fmt.Println("No value received")
    }
    }
    }

  • Summary:

    • Use of Select
    • Use of timer
    • Using nil Channel in Select

5. Traditional synchronization mechanism

  • In the complex example mentioned above, no lock is used, and the data is shared through the channel. This is the CSP mechanism.

  • There are also traditional mechanisms, such as mutex, cond and waitgroup

  • Mutex (mutex)

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    // Make the int type of the atomic operation. Note: the system has atomic operation. atomic.AddInt32(), etc. to ensure "thread safety",
    // go is to ensure security in multiple goroutine s executed concurrently
    type atomicInt struct {
    	val int
    	lock sync.Mutex
    }
    
    // Build two functions
    func (a *atomicInt) increment() {
    	// Protect with a lock
    	a.lock.Lock()
    	defer a.lock.Unlock()
    	a.val++
    
    }
    
    func (a *atomicInt) get() int {
    	// Lock unlock
    	a.lock.Lock()
    	defer a.lock.Unlock()
    	return a.val
    }
    
    func main() {
    	var a atomicInt
    	a.increment()
    	go func() {
    		a.increment()
    	}()
    	time.Sleep(time.Millisecond)
    	fmt.Println(a.get())
    }
    

  • The traditional synchronization mechanism is rarely used in g language, and channel is used for communication as much as possible. Traditional synchronization mechanisms communicate through shared memory and need to be locked for protection.

Chapter 12: a maze game, algorithm example

  • There is a file:

    // maze.in where the first line is the number of rows and columns of the file. The second line is separated by spaces. 
    // 0 represents a road and 1 represents a wall. Randomly give the starting point (start) and the ending point (end). Mark path
    
    6 5
    0 1 0 0 0
    0 0 0 1 0
    0 1 0 1 0
    1 1 1 0 0
    0 1 0 0 1
    0 1 0 0 0
    
  • code implementation

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    // 1. Read and return a two-dimensional array from the file
    func readMaze(filename string) [][]int {
    	// Open file
    	file, err := os.Open(filename)
    	if err != nil {
    		panic(err)
    	}
    
    	// Read rows and columns of two-dimensional array
    	var row, col int
    	fmt.Fscanf(file, "%d %d", &row, &col)  // To add the access address, the value transfer cannot change the external address
    	maze := make([][]int, row)  // Creating a two-dimensional array is actually a one-dimensional array, and the elements are one-dimensional arrays
    	for i := range maze {
    		maze[i] = make([]int, col)
    		for j := range maze[i] {   // Read index
    			fmt.Fscanf(file, "%d", &maze[i][j])
    		}
    	}
    	return maze
    }
    
    // Defines the representation of coordinate points
    type point struct {
    	i, j int
    }
    
    // Defines the direction of top left bottom right
    var dirs = [4]point {
    	{-1, 0}, {0, -1}, {1, 0}, {0, 1}}
    
    // Method for realizing coordinate addition
    func (p point) add(r point) point{  // Returns a new value using the value type
    	return point{p.i + r.i, p.j + r.j}
    }
    
    // Judge whether the point position is out of bounds
    func (p point) at(grid [][]int) (int, bool) {
    	if p.i < 0 || p.i >= len(grid) {
    		return 0, false
    	}
    
    	if p.j < 0 || p.j >= len(grid[p.i]) {
    		return 0, false
    	}
    
    	return grid[p.i][p.j], true
    }
    
    func walk(maze [][]int, start, end point) [][]int{
    	// Establish coordinate route
    	steps := make([][]int, len(maze))
    	for i := range steps {
    		steps[i] = make([]int, len(maze[i]))
    	}
    
    	// Breadth first solution
    	// 1. Establish a queue
    	Q := []point {start}
    	for len(Q) > 0 {
    		// Get the team head element
    		cur := Q[0]
    		Q = Q[1:]
    		// Gets the location of the element
    		curSteps, _ := cur.at(steps)
    		// At the end:
    		if cur == end { break }
    		// Go up, down, left and right
    		for _, dir := range dirs {
    			// Get new node location
    			next := cur.add(dir)
    
    			// Judge whether the position is legal, cross the boundary or hit the wall
    			val, ok := next.at(maze)
    			if !ok || val == 1 { continue }
    			// Judge whether this point has passed
    			val, ok = next.at(steps)
    			if !ok || val != 0 { continue }
    			// Not even equal to the origin
    			if next == start {continue}
    
    			// Let's start work, record the next element step number, and then add it to the Q queue
    			steps[next.i][next.j] =
    				curSteps + 1
    			Q = append(Q, next)
    		}
    	}
    
    	return steps
    }
    
    func main() {
    	// Get 2D slice
    	maze := readMaze("maze/maze.in")
    	// Print
    	//for _, row := range maze {
    	//	for _, num := range row {
    	//		fmt.Printf("%d ", num)
    	//	}
    	//	fmt.Println()
    	//}
    
    	// Labyrinth 
    	steps := walk(maze, point{0, 0}, point{
    		2,2})
    	// Print results
    	for _, row := range steps {
    		for _, num := range row {
    			fmt.Printf("%4d", num)   // %4d the last output occupies three bits
    		}
    		fmt.Println()
    	}
    }
    
    

Chapter 13: Standard Library

http Library

  • As a network-oriented, service-oriented and highly concurrent programming language, go language encapsulates http very well.

  • Can act as a server

  • It can also be used as a client: crawler, request

    • Send request using http client
    • Use http.Client to control request headers, etc
    • Simplify work with httputil
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"net/http/httputil"
    )
    
    func main() {
    	request, err := http.NewRequest(http.MethodGet, "http://www.imooc.com", nil)
    	request.Header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
    	// Custom Client
    	client := http.Client{
    		CheckRedirect: func(
    			req *http.Request,
    			via []*http.Request) error {
    			fmt.Println("Redirect: ", req)
    			return nil
    		},
    	}
    	resp, err := client.Do(request)
    	//resp, err := http.DefaultClient.Do(request)
    	//resp, err := http.Get("http://www.imooc.com")
    
    	if err != nil {
    		panic(err)
    	}
    	defer resp.Body.Close()
    
    	s, err := httputil.DumpResponse(resp, true)  // One time analysis
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Printf("%s\n", s)
    }
    
  • Performance analysis of http server

    • inport _ "net/http/pprof" is not in the program, but in the terminal. So add one_ Indicates use
    • Visit / debug/pprof
    • Use go tool pprof

2. Other standard libraries

  • bufio

  • log

  • encoding/json

  • regexp

  • time

    • time.Sleep()
    • Calculation time, etc
    • Generate channel, timer, etc
  • strings/ math/ rand

  • How to view a standard library document

  • Start a server yourself

    • godoc -http:8888 (I can't start it)
  • https://studygolang.com/pkgdoc

  • Create a queue: https://studygolang.com/pkgdoc

  • String to number, number to string: https://blog.csdn.net/weixin_34185560/article/details/91438265

  • String splicing (delete one of the characters)

    st := "12345678"
    st = st[:2] + st[3:8]
    fmt.Println(st)
    
    // 1245678
    

    //Splicing
    rst := []string {"1", "2", "3"}
    fmt.Println(strings.Join(rst, ","))

    // 1,2,3

  • Some small summary
package main

import "fmt"

type a struct {
	b int
}

type w struct {
	s *a
}

func index(x int) {      // 1. The defined parameters may not be used
	fmt.Println(123)
}

func main() {
	index(2)    // Defining parameters may not be used

	aa := a{1}
	w := w{&aa}

	w.s.b = 3    // 2. The structure is also a value transfer, which needs to be passed by pointer to be the same copy
	fmt.Println(w.s)  // Result &{3}
	fmt.Println(aa)		// Result {3}
}

[][]int{
//Establish coordinate route
steps := make([][]int, len(maze))
for i := range steps {
steps[i] = make([]int, len(maze[i]))
}

// Breadth first solution
// 1. Establish a queue
Q := []point {start}
for len(Q) > 0 {
	// Get the team head element
	cur := Q[0]
	Q = Q[1:]
	// Gets the location of the element
	curSteps, _ := cur.at(steps)
	// At the end:
	if cur == end { break }
	// Go up, down, left and right
	for _, dir := range dirs {
		// Get new node location
		next := cur.add(dir)

		// Judge whether the position is legal, cross the boundary or hit the wall
		val, ok := next.at(maze)
		if !ok || val == 1 { continue }
		// Judge whether this point has passed
		val, ok = next.at(steps)
		if !ok || val != 0 { continue }
		// Not even equal to the origin
		if next == start {continue}

		// Let's start work, record the next element step number, and then add it to the Q queue
		steps[next.i][next.j] =
			curSteps + 1
		Q = append(Q, next)
	}
}

return steps

}

func main() {
//Get 2D slice
maze := readMaze("maze/maze.in")
//Print
//for _, row := range maze {
// for _, num := range row {
// fmt.Printf("%d ", num)
// }
// fmt.Println()
//}

// Labyrinth 
steps := walk(maze, point{0, 0}, point{
	2,2})
// Print results
for _, row := range steps {
	for _, num := range row {
		fmt.Printf("%4d", num)   // %4d the last output occupies three bits
	}
	fmt.Println()
}

}

​

## Chapter 13: Standard Library

### http Library

* go Language as a network-oriented, service-oriented, high concurrency programming language http The packaging is very good.

* Can act as a server

* It can also be used as a client: crawler, request

* use http client poke request
* use http.Client Control request header, etc
* use httputil Simplify work

```go
package main

import (
	"fmt"
	"net/http"
	"net/http/httputil"
)

func main() {
	request, err := http.NewRequest(http.MethodGet, "http://www.imooc.com", nil)
	request.Header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
	// Custom Client
	client := http.Client{
		CheckRedirect: func(
			req *http.Request,
			via []*http.Request) error {
			fmt.Println("Redirect: ", req)
			return nil
		},
	}
	resp, err := client.Do(request)
	//resp, err := http.DefaultClient.Do(request)
	//resp, err := http.Get("http://www.imooc.com")

	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	s, err := httputil.DumpResponse(resp, true)  // One time analysis
	if err != nil {
		panic(err)
	}

	fmt.Printf("%s\n", s)
}
  • Performance analysis of http server

    • inport _ "net/http/pprof" is not in the program, but in the terminal. So add one_ Indicates use
    • Visit / debug/pprof
    • Use go tool pprof

2. Other standard libraries

  • bufio

  • log

  • encoding/json

  • regexp

  • time

    • time.Sleep()
    • Calculation time, etc
    • Generate channel, timer, etc
  • strings/ math/ rand

  • How to view a standard library document

  • Start a server yourself

    • godoc -http:8888 (I can't start it)
  • https://studygolang.com/pkgdoc

  • Create a queue: https://studygolang.com/pkgdoc

  • String to number, number to string: https://blog.csdn.net/weixin_34185560/article/details/91438265

  • String splicing (delete one of the characters)

    st := "12345678"
    st = st[:2] + st[3:8]
    fmt.Println(st)
    
    // 1245678
    

    //Splicing
    rst := []string {"1", "2", "3"}
    fmt.Println(strings.Join(rst, ","))

    // 1,2,3

  • Some small summary
package main

import "fmt"

type a struct {
	b int
}

type w struct {
	s *a
}

func index(x int) {      // 1. The defined parameters may not be used
	fmt.Println(123)
}

func main() {
	index(2)    // Defining parameters may not be used

	aa := a{1}
	w := w{&aa}

	w.s.b = 3    // 2. The structure is also a value transfer, which needs to be passed by pointer to be the same copy
	fmt.Println(w.s)  // Result &{3}
	fmt.Println(aa)		// Result {3}
}

  • FMT. Printf ('% p') can print memory addresses.

Topics: Go Back-end