Go language learning notes

Go language learning notes

Go language features

  • Simple, fast and safe
  • Parallel, interesting, open source
  • Memory management, array security, fast compilation

The main features of Go language

  • Automatic garbage collection
  • Richer built-in types
  • Function multiple return values
  • error handling
  • Anonymous functions and closures
  • Type and interface
  • Concurrent programming
  • reflex
  • Language interactivity

advantage

It provides massive parallel support. For the field of high-performance distributed systems, Go language undoubtedly has higher development efficiency than most other languages.

Basic grammar

Go programs are organized through package s.
Only the source file with package name main can contain the main function.
An executable has only one main package.
Import other non main packages through the import keyword.
You can import a single through the import keyword:

import "fmt"
import "io"

You can also import multiple at the same time:

import (
    "fmt"
    "math"
)

The const keyword is used to define constants.

// Constant definition
const PI = 3.14

Declare and assign global variables by using the var keyword outside the function body.

// Declaration and assignment of global variables
var name = "gopher"

Use the type keyword to declare structures and interfaces.

// General type declaration
type newType int
// Declaration of structure
type gopher struct{}
// Declaration of interface
type golang interface{}

Use the func keyword to declare a function.

func main() {
    Println("Hello World!")
}

Visibility rules:
In Go language, case is used to determine whether the constant, variable, type, interface, structure or function can be called by an external package.

The first lowercase letter of the function name is private:

func getId() {}

The initial capital of the function name is public:

func Printf() {}

data type

In go version 1.9, for numeric types, there is no need to define int, float32 and float64, and the system will recognize them automatically.

package main
import "fmt"

func main() {
   var a = 1.5
   var b =2
   fmt.Println(a,b)
}

The type of Boolean value is bool, the value is true or false, and the default is false.

//Sample code
var isActive bool  // Global variable declaration
var enabled, disabled = true, false  // Ignore declaration of type
func test() {
    var available bool  // General disclosures
    valid := false      // Short statement
    available = true    // Assignment operation
}

variable

The general form of declaring variables is to use the var keyword:

var identifier type

Declare multiple variables

var identifier1, identifier2 type

Variable declaration
First, specify the variable type. If the variable is not initialized, the variable defaults to zero (the value set by the system by default when the variable is not initialized).

  • The value type (including complex64/128) is 0
  • Boolean type is false
  • String is' '(empty string)
  • The following types are nil:
    var a *int
    var a []int
    var a map[string] int
    var a chan int
    var a func(string) int
    var a error // error is the interface
    

The second is to determine the type of variable according to the value.

var v_name = value

Third, if the variable has been declared with var and then declared with: =, a compilation error will be generated. Format:

v_name := value
// Intval: = 1 equals:
var intVal int 
intVal =1 

ps:

var (  // This factorization keyword is generally used to declare global variables
    a int
    b bool
)

//This kind of without declaration format can only appear in the function body
//g, h := 123, "hello"
func main(){
    g, h := 123, "hello"
    ...
}

Blank identifier (a write only variable, its value cannot be obtained):

package main

import "fmt"

func main() {
  _,numb,strs := numbers() //Get only the last two values returned by the function
  fmt.Println(numb,strs)
}

//A function that can return multiple values
func numbers()(int,int,string){
  a , b , c := 1 , 2 , "str"
  return a,b,c
}

constant

A const ant is an identifier of a simple value that will not be modified when the program runs.
The data types in constants can only be Boolean, numeric (integer, floating point and complex) and string.
Definition format of constant:

const identifier [type] = value

When defining a constant group, if no initial value is provided, it means that the upstream expression will be used.

package main

import "fmt"

const (
    a = 1
    b
    c
    d
)

func main() {
    fmt.Println(a)
    // b. c and d are not initialized, and the value of the previous line (i.e. a) is used
    fmt.Println(b)   // Output 1
    fmt.Println(c)   // Output 1
    fmt.Println(d)   // Output 1
}

Iota, a special constant, can be considered as a constant that can be modified by the compiler (incremented in the same const constant group. The iota count will restart whenever there is a new const keyword).
Usage:

package main

import "fmt"

func main() {
    const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //Independent value, iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7. Resume counting
            i          //8
    )
    const xx = iota
	const yy = iota
    fmt.Println(a,b,c,d,e,f,g,h,i,xx,yy)
    // The output is 0 1 2 ha 100 ha 100 7 8 0 0
}

operator

operatordescribeexample
&Return variable storage address&a; The actual address of the variable will be given.
*Pointer variable*a; Is a pointer variable

Difference between pointer variable * and address value &:
Pointer variables store an address value and allocate independent memory to store an integer number. When the variable is marked with * in front, it is equivalent to the usage of &, otherwise an integer number will be output directly.

func main() {
   var a int = 4
   var ptr *int
   ptr = &a
   println("a The value of is", a);    // 4
   println("*ptr by", *ptr);  // 4
   println("ptr by", ptr);    // 824633794744
}

Conditional statement

Another powerful feature of Go's if is that it is allowed to declare a variable in the conditional judgment statement. The scope of this variable can only be within the conditional logic block, and other places will not work, as shown below:

package main
  
import "fmt"
func main() {
    if num := 9; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has 1 digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }
}
/*
Operation results:
9 has 1 digit
*/

if statement uses tips:

  • You do not need parentheses to enclose conditions
  • Braces {} must exist, even if there is only one line of statement
  • The left parenthesis must be on the same line as if or else
  • After if and before conditional statements, you can add variable initialization statements, using; Separate
  • In a function with a return value, the final return cannot be in a conditional statement

The switch statement uses tips:

  • Support multi condition matching
    switch{
        case 1,2,3,4:
        default:
    }
    
  • Different cases are not separated by break, and only one case will be executed by default.
  • If you want to execute multiple case s, you need to use the fallthrough keyword, or you can terminate with break.
    switch{
        case 1:
        ...
        if(...){
            break
        }
    
        fallthrough // At this point, switch(1) will execute case1 and case2, but if the if condition is met, only case1 will be executed
    
        case 2:
        ...
        case 3:
    }
    

select statement:
Similar to the switch statement used for communication. Each case must be a communication operation, either send or receive.

  • Each case must be a communication
  • All channel expressions are evaluated
  • All expressions sent are evaluated
  • If any one communication can be carried out, it will be executed and the others will be ignored.
  • If multiple case s can be run, Select will randomly and fairly Select one to execute. Others will not be implemented.
    Otherwise:
    1. If there is a default clause, the statement is executed.
    2. If there is no default clause, select will block until a communication can run; Go does not re evaluate the channel or value.
    package main
    
    import "fmt"
    
    func main() {
       var c1, c2, c3 chan int
       var i1, i2 int
       select {
          case i1 = <-c1:
             fmt.Printf("received ", i1, " from c1\n")
          case c2 <- i2:
             fmt.Printf("sent ", i2, " to c2\n")
          case i3, ok := (<-c3):  // same as: i3, ok := <-c3
             if ok {
                fmt.Printf("received ", i3, " from c3\n")
             } else {
                fmt.Printf("c3 is closed\n")
             }
          default:
             fmt.Printf("no communication\n")
       }    
    }
    /*
    Execution results:
    no communication
    */
    

Circular statement

There are three forms of For loop:
Like for in C language:

for init; condition; post { }

Like C's while:

for condition { }

for() of and C; 😉 Same:

for { }
  • init: it is generally an assignment expression, which assigns an initial value to the control variable;
  • Condition: relational expression or logical expression, loop control condition;
  • post: it is generally an assignment expression, which increments or decrements the control variable.
    For each range loop:
    The loop in this format can iterate over strings, arrays, slices, etc. and output elements.
package main
import "fmt"

func main() {
	strings := []string{"google", "runoob"}
	for i, s := range strings {
		fmt.Println(i, s)
	}
	numbers := [6]int{1, 2, 3, 5}
	for i,x:= range numbers {
		fmt.Printf("The first %d position x Value of = %d\n", i,x)
	}  
	nameMap := make(map[string]string)
	nameMap["first name"] = "Hayes"
	nameMap["last name"] = "Peng"
	for k, v := range nameMap {
		fmt.Println(k, v)
	}
}
/*
Output results:
0 google
1 runoob
 Value of bit 0 x = 1
 Value of bit 1 x = 2
 Value of bit 2 x = 3
 Value of bit 3 x = 5
 Value of bit 4 x = 0
 Value of bit 5 x = 0
last name Peng
first name Hayes

*/

function

Function returns multiple values:

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Google", "Runoob")
   fmt.Println(a, b)
}

Parameter transfer method:

Delivery typedescribe
pass by valueValue passing refers to copying and passing a copy of the actual parameters to the function when calling the function, so that if the parameters are modified in the function, the actual parameters will not be affected.
Reference passingReference passing refers to passing the address of the actual parameter to the function when calling the function. The modification of the parameter in the function will affect the actual parameter.
package main

import "fmt"

func main() {
   /* Define local variables */
   var a int = 100
   var b int= 200

   fmt.Printf("Before the exchange, a Value of : %d\n", a )
   fmt.Printf("Before the exchange, b Value of : %d\n", b )

   valueSwap(a, b);
   
   fmt.Printf("After value transfer and exchange, a Value of : %d\n", a )
   fmt.Printf("After value transfer and exchange, b Value of : %d\n", b )
   
   /* Call swap() function
   * &a Pointer to a, address of a variable
   * &b Pointer to b, address of b variable
   */
   swap(&a, &b)

   fmt.Printf("After reference passing exchange, a Value of : %d\n", a )
   fmt.Printf("After reference passing exchange, b Value of : %d\n", b )
}

func valueSwap(x, y int) {
   var temp int

   temp = x /* Save the value of x */
   x = y    /* Assign y value to x */
   y = temp /* Assign the temp value to y*/
}

func swap(x *int, y *int) {
   var temp int
   temp = *x    /* Save value on x address */
   *x = *y      /* Assign y value to x */
   *y = temp    /* Assign the temp value to y */
}
/*
Output results:
Before exchange, the value of a: 100
 Value of b before exchange: 200
 After value transfer and exchange, the value of a: 100
 After value transfer and exchange, the value of b: 200
 After reference passing exchange, the value of a: 200
 After reference passing exchange, the value of b: 100
*/

Function as argument:

package main

import (
   "fmt"
   "math"
)

// Declare function type
type cb func(int) int

func main() {
	
	/* Declare function variables */
   	getSquareRoot := func(x float64) float64 {
      	return math.Sqrt(x)
   	}

   	/* Use function */
   	fmt.Println(getSquareRoot(9))
	
    testCallBack(1, callBack)
    testCallBack(2, func(x int) int {
        fmt.Printf("I'm a callback, x: %d\n", x)
        return x
    })
}

func testCallBack(x int, f cb) {
    f(x)
}

func callBack(x int) int {
    fmt.Printf("I'm a callback, x: %d\n", x)
    return x
}
/*
Output results:
3
 I am a callback, x: 1
 I'm a callback, x: 2
*/

Function closure:
Go language supports anonymous functions and can be used as closures. An anonymous function is an "inline" statement or expression. The advantage of anonymous function is that it can directly use the variables in the function without declaration.

package main

import "fmt"

// Closure usage
/*
func add(x1, x2 int) func(x3 int, x4 int)(int, int, int)  {
    i := 0
    return func(x3 int, x4 int) (int, int, int){ 
       i++
       return i, x1 + x2, x3 + x4
    }
}
*/

// Closures use methods, and the return value (closure function) in the function declaration does not need to write a specific formal parameter name
func add(x1, x2 int) func(int, int) (int, int, int) {
  i := 0
  return func(x3, x4 int) (int, int, int) {
    i += 1
    return i, x1 + x2, x3 + x4
  }
}

func main() {
  add_func := add(1, 2)
  fmt.Println(add_func(4, 5))
  fmt.Println(add_func(1, 3))
  fmt.Println(add_func(2, 2)) 
}
/*
Output results:
1 3 9
2 3 4
3 3 4
*/

Function method:
There are both functions and methods in Go language. A method is a function that contains a receiver, which can be a value or a pointer of a named type or struct type.
With regard to values and pointers, if you want to change the properties of the structure type in the method, you need to pass a pointer to the method.

package main

import (
   "fmt"  
)

/* Define structure */
type Circle struct {
  radius float64
}


func main()  { 
   var c Circle
   fmt.Println(c.radius)
   c.radius = 10.00
   fmt.Println("Area of circle = ", c.getArea())
   c.changeRadius2(20)
   fmt.Println(c.radius)
   c.changeRadius(20)
   fmt.Println(c.radius)
   change(&c, 30)
   fmt.Println(c.radius)
}
//The method belongs to a method in an object of type Circle
func (c Circle) getArea() float64  {
   //c.radius is an attribute in an object of type Circle
   return 3.14 * c.radius * c.radius
}
// Note that if you want to change the value of successful c, you need to pass the pointer here
func (c *Circle) changeRadius(radius float64)  {
   c.radius = radius
}

// The following actions will not take effect
func (c Circle) changeRadius2(radius float64)  {
   c.radius = radius
}

// To change the value of a reference type, you need to pass a pointer
func change(c *Circle, radius float64)  {
   c.radius = radius
}
/*
Output results:
0
 Area of circle =  314
10
20
30

All parameters are passed by value: slice, map and channel will have the illusion of passing references (for example, the slice corresponds to an array behind it. The slice itself is a data structure that contains a pointer to the array. Therefore, even when the value is passed, the structure is copied into the function. When the value of the array is operated through the pointer, the same space is actually operated, and the structure is changed Copied, but the pointer contained in the structure points to the same array, so there is this illusion)

Variable scope:

  • Local variables: variables declared in the function body are called local variables. Their scope is only in the function body. Parameters and return value variables are also local variables.
  • Global variables: variables declared outside the function are called global variables. Global variables can be used in the whole package or even external packages (after being exported).
  • Formal parameters: formal parameters are used as local variables of functions
package main

import "fmt"

/* Declare global variables */
var a int = 20

func main() {
    /* main Declaring local variables in a function */
    var a int = 10
    var b int = 20
    var c int = 0

    fmt.Printf("main()In function a = %d\n", a)
    c = sum(a, b)
    fmt.Printf("main()In function a = %d\n", a)
    fmt.Printf("main()In function c = %d\n", c)
}

/* Function definition - adding two numbers */
func sum(a, b int) int {
    a = a + 1
    fmt.Printf("sum() In function a = %d\n", a)
    fmt.Printf("sum() In function b = %d\n", b)
    return a + b
}
/*
Output results:
main()a = 10 in function
sum() a = 11 in function
sum() b = 20 in function
main()a = 10 in function
main()c = 31 in function
*/

array

Declaration array

var variable_name [SIZE] variable_type
var balance [10] float32

Initialize array

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
// Declaration + initialization
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
// If the length of the array is uncertain, you can use... Instead of the length of the array
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

Multidimensional array

// Declaration method
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type{}
// Declaration + initialization
a := [3][4]int{  
 {0, 1, 2, 3} ,   /*  The first row index is 0 */
 {4, 5, 6, 7} ,   /*  The index of the second row is 1 */
 {8, 9, 10, 11},   /* The third row has an index of 2 */
}

Passing an array to a function

package main

import (
    "fmt"
)

func main() {
    var array = []int{1, 2, 3, 4, 5}
    /* An array (slice) with undefined length can only be passed to a function that does not limit the length of the array */
    setArray(array)
    /* An array with a defined length can only be passed to a function that limits the length of the same array */
    var array2 = [5]int{1, 2, 3, 4, 5}
    setArray2(array2)
}

func setArray(params []int) {
    fmt.Println("params array length of setArray is : ", len(params))
}

func setArray2(params [5]int) {
    fmt.Println("params array length of setArray2 is : ", len(params))
}

Array (there is no so-called array without declared length) is a copy passed as a function parameter. Modifying the array in the function does not change the original array.

package main
import "fmt"
func change(nums[3] int){
   nums[0]=100
}
func main() {
    var nums=[3]int{1,2,3}   
    change(nums)   //nums has not been changed   
    fmt.Println(nums[0])   
    return
}
// Output result: 1

The difference between array and slice

  • The array of Go language is a value, and its length is a part of its type. When it is used as a function parameter, it is passed by value, and the modifications in the function are not visible to the caller

  • The processing of arrays in Go language generally adopts the method of slicing. Slicing contains a reference to the contents of the underlying array. When used as a function parameter, it is similar to pointer passing, and the modifications in the function are visible to the caller

package main

import "fmt"

func main() {
   // array
    b := [...]int{2, 3, 5, 7, 11, 13}
    boo(b)
    fmt.Println(b) // [2 3 5 7 11 13]
    
    // section
    p := []int{2, 3, 5, 7, 11, 13}
    poo(p)
    fmt.Println(p)  // [13 3 5 7 11 2]
}

func boo(tt [6]int) {
    tt[0], tt[len(tt)-1] = tt[len(tt)-1], tt[0]
}

func poo(tt []int) {
    tt[0], tt[len(tt)-1] = tt[len(tt)-1], tt[0]
}

Alternatively, you can pass an array reference to a function.

package main
import "fmt"
// The array of Go language is a value, and its length is a part of its type. When it is used as a function parameter, it is passed by value, and the modifications in the function are not visible to the caller
func change1(nums [3]int) {    
    nums[0] = 4
}
// Pass in the memory address of the array, and then define the pointer variable to point to the address, which will change the value of the array
func change2(nums *[3]int) {    
    nums[0] = 5
}
// The processing of arrays in Go language generally adopts the method of slicing. Slicing contains a reference to the contents of the underlying array. When used as a function parameter, it is similar to pointer passing, and the modifications in the function are visible to the caller
func change3(nums []int) {    
    nums[0] = 6
}
func main() {    
    var nums1 = [3]int{1, 2, 3}   
    var nums2 = []int{1, 2, 3}    
    change1(nums1)    
    fmt.Println(nums1)  //  [1 2 3]     
    change2(&nums1)    
    fmt.Println(nums1)  //  [5 2 3]    
    change3(nums2)    
    fmt.Println(nums2)  //  [6 2 3]
}

Pointer

&Is to take the address character and put it in front of a variable. It will return the memory address of the corresponding variable.

package main

import "fmt"

func main() {
   var a int= 20   /* Declare actual variables */
   var ip *int        /* Declare pointer variables */

   ip = &a  /* Storage address of pointer variable */
   fmt.Printf("a The address of the variable is: %x\n", &a  )

   /* Storage address of pointer variable */
   fmt.Printf("ip Pointer address of variable storage: %x\n", ip )

   /* Accessing values using pointers */
   fmt.Printf("*ip Value of variable: %d\n", *ip )
}
/*
Output results:
a The address of the variable is c0000016060
ip Pointer address of variable storage: c0000016060
*ip Value of variable: 20
*/

Pointer array & & array pointer

var arr [3]int
var parr [3]*int // Pointer array
var p *[3]int = &arr // Array pointer
for k, _ := range arr {
    parr[k] = &arr[k];
}

// Output address comparison
for i := 0; i < 3; i+=1 {
    fmt.Println(&arr[i], parr[i], &(*p)[i]);
}

Pointer to pointer
If a pointer variable stores the address of another pointer variable, the pointer variable is called the pointer variable pointing to the pointer.

package main

import "fmt"

func main() {

   var a int
   var ptr *int
   var pptr **int

   a = 3000

   /* Pointer ptr address */
   ptr = &a

   /* Pointer ptr address */
   pptr = &ptr

   /* Gets the value of pptr */
   fmt.Printf("variable a = %d\n", a )
   fmt.Printf("Pointer variable *ptr = %d\n", *ptr )
   fmt.Printf("Pointer variable to pointer **pptr = %d\n", **pptr)
}
/*
Output results:
Variable a = 3000
 Pointer variable * ptr = 3000
 Pointer variable pointing to pointer * * pptr = 3000
*/

Pointer as function parameter

package main

import "fmt"

func main() {
   /* Define local variables */
   var a int = 100
   var b int= 200

   fmt.Printf("Before exchange a Value of : %d\n", a )
   fmt.Printf("Before exchange b Value of : %d\n", b )

   /* Call the function to exchange values
   * &a Address to a variable
   * &b Address to b variable
   */
   swap(&a, &b);
   // Same as a, b = b, a

   fmt.Printf("After exchange a Value of : %d\n", a )
   fmt.Printf("After exchange b Value of : %d\n", b )
}

func swap(x *int, y *int) {
   *x, *y = *y, *x
}
/*
Output results:
Value of a before exchange: 100
 Value of b before exchange: 200
 Value of a after exchange: 200
 Value of b after exchange: 100
*/

structural morphology

Struct is similar to a class in java. Member variables can be defined in struct.

It can be initialized in a way similar to the java constructor

There are two ways to access member variables:

  1. Accessed through struct variable. Member variable.
  2. Accessed through struct pointer. Member variable.

You do not need to set access permissions through getters and setters.

package main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}


func main() {

    // Create a new structure
    fmt.Println(Books{"Go language", "www.runoob.com", "Go Language course", 6495407})

    // You can also use the format key = > value
    fmt.Println(Books{title: "Go language", author: "www.runoob.com", subject: "Go Language course", book_id: 6495407})

    // The ignored field is 0 or empty
    fmt.Println(Books{title: "Go language", author: "www.runoob.com"})
    
    var Book1 Books        /* Declare Book1 as a Books type */
    
    /* book 1 describe */
    Book1.title = "Go language"
    Book1.author = "www.runoob.com"
    Book1.subject = "Go Language course"
    Book1.book_id = 6495407
   
    /* Print Book1 information */
    fmt.Printf( "Book 1 title : %s\n", Book1.title)
    fmt.Printf( "Book 1 author : %s\n", Book1.author)
    fmt.Printf( "Book 1 subject : %s\n", Book1.subject)
    fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id)
    fmt.Println(Book1)
}
/*
Output results:
{Go Language www.runoob.com Go language tutorial 6495407}
{Go Language www.runoob.com Go language tutorial 6495407}
{Go Language: www.runoob.com 0}
Book 1 title : Go language
Book 1 author : www.runoob.com
Book 1 subject : Go Language course
Book 1 book_id : 6495407
{Go Language www.runoob.com Go language tutorial 6495407}
*/

The structure is passed as the value of the parameter. If you want to change the data content of the structure in the function, you need to pass in the pointer

package main

import "fmt"

type Books struct {
    title string
    author string
    subject string
    book_id int
}

func changeBook(book Books) {
    book.title = "book1_change"
}

func changeBook1(book *Books) {
    book.title = "book1_change"
}

func main() {
    var book1 Books
    book1.title = "book1"
    book1.author = "zuozhe"
    book1.book_id = 1
    changeBook(book1)
    fmt.Println(book1)
    changeBook1(&book1)
    fmt.Println(book1)
}
/*
Output results:
{book1 zuozhe  1}
{book1_change zuozhe  1}
*/

The case of the first letter of an attribute in a structure

  • The initial capital letter is equivalent to public.
  • The initial lowercase is equivalent to private.

Note: this public and private is relative to the package (the package name followed by the package in the first line of the go file).

Knock on the blackboard and draw the key points

When a structure object is to be converted to JSON, the first letter of the attribute in the object must be uppercase in order to be converted to JSON normally.

Example 1:

type Person struct {
   Name string      //The Name field is capitalized
   age int               //age field initial lowercase
}

func main() {
  person:=Person{"Xiao Ming",18}
  if result,err:=json.Marshal(&person);err==nil{  //json.Marshal converts an object to a json string
    fmt.Println(string(result))
  }
}
/*
Output results:
{"Name":"Xiao Ming "} / / only Name, not age
*/

Example 2:

type Person  struct{
     Name  string      //All capitalized
     Age    int               
}
/*
Output results:
{"Name":"Xiaoming "," Age":18} / / both fields
*/

So the JSON string can only be capitalized in the future? Of course not, you can use tag to mark the field name to be returned.

Example 3:

type Person  struct{
     Name  string   `json:"name"`   //The tag json name is name
     Age    int     `json:"age"`
     Time int64    `json:"-"`        // The tag ignores this field

}

func main(){
  person:=Person{"Xiao Ming",18, time.Now().Unix()}
  if result,err:=json.Marshal(&person);err==nil{
   fmt.Println(string(result))
  }
}
/*
Output results:
{"name":"Xiao Ming "," age":18}
*/

section

Go language slicing is an abstraction of arrays.

The length of the go array cannot be changed, and such a collection is not applicable in a specific scene. Go provides a flexible and powerful built-in type slice ("dynamic array"). Compared with the array, the length of the slice is not fixed, and elements can be added, which may increase the capacity of the slice.
len() and cap() functions

Slices are indexable, and the length can be obtained by the len() method.
Slicing provides a method to calculate the capacity. cap() can measure the maximum length of slicing.
The following are specific examples:

package main

import "fmt"

func main() {
   // make([]T, length, capacity)
   var numbers = make([]int,3,5)

   printSlice(numbers)
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
/*
Output results:
len=3 cap=5 slice=[0 0 0]
*/

Empty (nil) slice
A slice defaults to nil and its length is 0 before initialization. The example is as follows:

package main

import "fmt"

func main() {
   var numbers []int

   printSlice(numbers)

   if(numbers == nil){
      fmt.Printf("The slice is empty")
   }
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
/*
Output results:
len=0 cap=0 slice=[]
The slice is empty
*/

Slice interception
You can set the cut slice [lower bound: upper bound] by setting the lower bound and upper bound
For slice slice[i:j] whose underlying array capacity is k, the new slice length and capacity:

length: j-i
 capacity: k-i

Examples are as follows:

package main

import "fmt"

func main() {
   /* Create slice */
   numbers := []int{0,1,2,3,4,5,6,7,8}  
   printSlice(numbers)

   /* Print original slice */
   fmt.Println("numbers ==", numbers)

   /* Print sub slice from index 1 (included) to index 4 (not included)*/
   fmt.Println("numbers[1:4] ==", numbers[1:4])

   /* The default lower limit is 0*/
   fmt.Println("numbers[:3] ==", numbers[:3])

   /* The default upper limit is len(s)*/
   fmt.Println("numbers[4:] ==", numbers[4:])

   numbers1 := make([]int,0,5)
   printSlice(numbers1)

   /* Print sub slice from index 0 (included) to index 2 (not included) */
   number2 := numbers[:2]
   printSlice(number2)

   /* Print sub slice from index 2 (included) to index 5 (not included) */
   number3 := numbers[2:5]
   printSlice(number3)

}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
/*
Output results:
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]
*/

append() and copy() functions
If we want to increase the capacity of the slice, we must create a new and larger slice and copy the contents of the original slice.
The following code describes the copy method for copying slices and the append method for appending new elements to slices.
When append(list, [params]), first judge whether the cap length of the list is greater than or equal to len(list) + len([params]). If it is greater than, the cap remains unchanged, otherwise:

When adding multiple elements at the same time:

  • If len[list]+len[params] is an even number, cap=len[list]+len[params]
  • If len[list]+len[params] is an odd number, then cap=len[list]+len[params]+1

When adding elements one by one:

  • len(list)+1<=cap: cap=cap

  • len(list)+1>cap: cap=2*cap

package main

import "fmt"

func main() {
   var numbers []int
   printSlice(numbers)

   /* Allow appending empty slices */
   numbers = append(numbers, 0)
   printSlice(numbers)

   /* Add an element to the slice */
   numbers = append(numbers, 1)
   printSlice(numbers)

   /* Add multiple elements at the same time */
   numbers = append(numbers, 2,3,4)
   printSlice(numbers)

   /* Creating slice numbers1 is twice the capacity of the previous slice*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* Copy the contents of numbers to numbers1 */
   copy(numbers1,numbers)
   printSlice(numbers1)  
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
/*
Output results:
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]
*/

Range

package main
import "fmt"
func main() {
    //This is how we use range to find the sum of a slice. Using an array is very similar to this
    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)
    //Using range on the array will pass in two variables, index and value. In the above example, we do not need to use the sequence number of the element, so we use the blank character "_ "Omitted. Sometimes we really need to know its index.
    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }
    //range can also be used on key value pairs of map s.
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }
    //range can also be used to enumerate Unicode strings. The first parameter is the index of the character, and the second is the character (Unicode value) itself.
    for i, c := range "go" {
        fmt.Println(i, c)
    }
}
/*
Output results:
sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111
*/

Map (Collection)

package main

import "fmt"

func main() {
    var countryCapitalMap map[string]string /*Create collection */
    countryCapitalMap = make(map[string]string)

    /* map Insert the key - value pair and the corresponding capital of each country */
    countryCapitalMap [ "France" ] = "Paris"
    countryCapitalMap [ "Italy" ] = "Rome"
    countryCapitalMap [ "Japan" ] = "Tokyo"
    countryCapitalMap [ "India " ] = "New Delhi"

    /*Use the key to output map values */
    for country := range countryCapitalMap {
        fmt.Println(country, "The capital is", countryCapitalMap [country])
    }

    /*See if the element exists in the collection */
    capital, ok := countryCapitalMap [ "American" ] /*If it is determined to be true, it exists, otherwise it does not exist */
    /*fmt.Println(capital) */
    /*fmt.Println(ok) */
    if (ok) {
        fmt.Println("American Our capital is", capital)
    } else {
        fmt.Println("American Your capital does not exist")
    }
    
    /*Delete element*/ delete(countryCapitalMap, "France")
    fmt.Println("French entry deleted")

    fmt.Println("Map after deleting elements")

    /*Print map*/
    for country := range countryCapitalMap {
            fmt.Println(country, "The capital is", countryCapitalMap [ country ])
    }
}
/*
Output results:
France The capital is Paris
Italy The capital is Rome
Japan The capital is Tokyo
India  The capital is New Delhi
American Your capital does not exist
 French entry deleted
 Map after deleting elements
Italy The capital is Rome
Japan The capital is Tokyo
India  The capital is New Delhi
*/

Recursive function

Factorial

package main

import "fmt"

func Factorial(n uint64)(result uint64) {
    if (n > 0) {
        result = n * Factorial(n-1)
        return result
    }
    return 1
}

func main() {  
    var i int = 15
    fmt.Printf("%d The factorial of is %d\n", i, Factorial(uint64(i)))
}
/*
Output results:
15 The factorial of is 1307674368000
*/

Fibonacci sequence

package main

import "fmt"

func fibonacci(n int) int {
  if n < 2 {
   return n
  }
  return fibonacci(n-2) + fibonacci(n-1)
}

func main() {
    var i int
    for i = 0; i < 10; i++ {
       fmt.Printf("%d\t", fibonacci(i))
    }
}
/*
Output results:
0	1	1	2	3	5	8	13	21	34	
*/

Type conversion

package main

import "fmt"

func main() {
   var sum int = 17
   var count int = 5
   var mean float32
   /* 
   		go Implicit conversion type is not supported. An error will be reported if
   		mean = sum/float32(count)
   		mean = float32(sum)/count
   		mean = sum/count
   */
   mean = float32(sum)/float32(count)
   fmt.Printf("mean The value of is: %f\n",mean)
}
/*
Output results:
mean The value of is 3.400000
*/

Interface

package main

import (
    "fmt"
)

type Phone interface {
    call() string 
}

type Android struct {
    brand string
}

type IPhone struct {
    version string
}

func (android Android) call() string {
    return "I am Android " + android.brand
}

func (iPhone IPhone) call() string {
    return "I am iPhone " + iPhone.version
}

func printCall(p Phone) {
    fmt.Println(p.call() + ", I can call you!")
}

func main() {
    var vivo = Android{brand:"Vivo"}
    var hw = Android{"HuaWei"}

    i7 := IPhone{"7 Plus"}
    ix := IPhone{"X"}

    printCall(vivo)
    printCall(hw)
    printCall(i7)
    printCall(ix)
}
/*
Output results:
I am Android Vivo, I can call you!
I am Android HuaWei, I can call you!
I am iPhone 7 Plus, I can call you!
I am iPhone X, I can call you
*/

error handling

Go language provides a very simple error handling mechanism through the built-in error interface.
error type is an interface type. This is its definition:

type error interface {
    Error() string
}
package main

import (
    "fmt"
)

// Define a DivideError structure
type DivideError struct {
    dividee int
    divider int
}

// Implement the 'error' interface
func (de *DivideError) Error() string {
    strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
    return fmt.Sprintf(strFormat, de.dividee)
}

// Define a function for division of type 'int'
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
    if varDivider == 0 {
            dData := DivideError{
                    dividee: varDividee,
                    divider: varDivider,
            }
            errorMsg = dData.Error()
            return
    } else {
            return varDividee / varDivider, ""
    }

}

func main() {

    // Normal condition
    if result, errorMsg := Divide(100, 10); errorMsg == "" {
            fmt.Println("100/10 = ", result)
    }
    // An error message is returned when the divisor is zero
    if _, errorMsg := Divide(100, 0); errorMsg != "" {
            fmt.Println("errorMsg is: ", errorMsg)
    }

}
/*
Output results:
100/10 =  10
errorMsg is:  
    Cannot proceed, the divider is zero.
    dividee: 100
    divider: 0
*/

Panic and recover are two built-in functions of Go. These two built-in functions are used to handle errors during Go runtime, panic is used to actively throw errors, and recover is used to capture errors thrown by panic.

  • There are two cases of causing panic: one is that the program calls actively, and the other is that the program generates a runtime error, which is detected and exited by the runtime.
  • After a panic occurs, the following code will not be executed. The program will return immediately from the function where the panic is called or where the panic occurs, execute the defer statement of the function layer by layer (defer is similar to finally), and then print the function call stack layer by layer until it is captured by recover or run to the outermost function.
  • The panic can be thrown not only in the normal flow of the function, but also in the defer logic. The panic in the defer can be captured by the subsequent defer.
  • recover is used to capture the panic and prevent the panic from continuing to pass upward. recover() and defer are used together, but the defer can only be used directly in the following function body to catch the panic and terminate the exception. Otherwise, nil is returned and the exception continues to pass outward.

Multiple panic s capture only the last:

package main
import "fmt"
func main(){
    defer func(){
        if err := recover() ; err != nil {
            fmt.Println(err)
        }
    }()
    defer func(){
        panic("three")
    }()
    defer func(){
        panic("two")
    }()
    panic("one")
}
/*
Output results:
three
*/

be careful:

  • The code after the panic will not be executed before the panic uses recover or the level of function stack captured in recover; Once captured by recover, the outer function stack code returns to normal, and all code will be executed
  • When using recover to capture a panic, defer needs to be declared before the panic. Otherwise, the code after the panic cannot be executed, so it cannot be recovered
package main

import (
"fmt"
)

func main() {
  fmt.Println("Outer layer start")
  defer func() {
    fmt.Println("Outer layer preparation recover")
    if err := recover(); err != nil {
      fmt.Printf("%#v-%#v\n "," outer layer ", err) // err has been caught in the function of the upper level. There is no exception here. It is just a routine to execute defer first, and then execute the following code
    } else {
      fmt.Println("I didn't do anything")
    }
    fmt.Println("Outer layer completion recover")
  }()
  fmt.Println("The outer layer is about to be abnormal")
  f()
  fmt.Println("After outer layer abnormality")
  defer func() {
    fmt.Println("After outer layer abnormality defer")
  }()
}

func f() {
  fmt.Println("Inner layer start")
  defer func() {
    fmt.Println("Inner layer recover Former defer")
  }()

  defer func() {
    fmt.Println("Inner layer preparation recover")
    if err := recover(); err != nil {
      fmt.Printf("%#v-%#v\n "," inner layer ", err) / / here, err is the content passed in by panic
    }

    fmt.Println("Inner layer completion recover")
  }()

  defer func() {
    fmt.Println("Before inner anomaly recover Posterior defer")
  }()

  panic("Abnormal information")

  defer func() {
    fmt.Println("After inner layer abnormality defer")
  }()

  fmt.Println("Statement after inner exception") //recover captures the first level or no capture at all. Starting here, the following code will not be executed again
}
/*
Output results:
Outer layer start
 The outer layer is about to be abnormal
 Inner layer start
 defer after recover before inner exception
 Inner layer recover y
"Inner "-" exception information“
The inner layer completes the recover
 defer before inner layer recover
 After outer layer abnormality
 defer after outer exception
 External preparation recover
 I didn't do anything
 recover completed
*/

Concurrent

Go language supports concurrency. We only need to open goroutine through the go keyword.
Goroutine is a lightweight thread, and the scheduling of goroutine is managed by the Golang runtime.
All goroutine s in the same program share the same address space.

package main

import (
        "fmt"
        "time"
)

func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}

func main() {
        go say("world")
        say("hello")
}
// The output of hello and world has no fixed order, because they are executed by two goroutine s
/*
Output results:
hello
world
world
hello
hello
world
hello
world
hello
*/

channel
A channel is a data structure used to transfer data.
A channel can be used for synchronous operation and communication between two goroutine s by passing a value of a specified type. The operator < - is used to specify the direction of the channel, send or receive. If no direction is specified, it is a two-way channel.

ch <- v    // Send v to channel ch
v := <-ch  // Receive data from ch
           // And assign the value to v
// The sum of numbers is calculated by two goroutines. After goroutine completes the calculation, it will calculate the sum of the two results
package main

import "fmt"

func sum(s []int, c chan int) {
        sum := 0
        for _, v := range s {
                sum += v
        }
        c <- sum // Send sum to channel c
}

func main() {
        s := []int{7, 2, 8, -9, 4, 0}

        c := make(chan int)
        go sum(s[:len(s)/2], c)
        go sum(s[len(s)/2:], c)
        x, y := <-c, <-c // Received from channel c

        fmt.Println(x, y, x+y)
}
/*
Output results:
-5 17 12
*/

Channel buffer
By default, the channel does not have a buffer. The sender sends data and the receiver must receive data accordingly.
The channel can set the buffer, and specify the buffer size through the second parameter of make:
ch := make(chan int, 100)
The channel with buffer allows the data transmission of the sender and the data acquisition of the receiver to be asynchronous, that is, the data sent by the sender can be placed in the buffer and wait for the receiver to obtain the data, rather than requiring the receiver to obtain the data immediately.
However, because the size of the buffer is limited, there must be a receiver to receive data. Otherwise, as soon as the buffer is full, the data sender cannot send data again.
Note: if the channel is not buffered, the sender will block until the receiver receives the value from the channel. If the channel is buffered, the sender will block until the sent value is copied into the buffer. If the buffer is full, it means that it needs to wait until a receiver obtains a value. The receiver will block until there is a value to receive.

package main

import "fmt"

func main() {
    // Here we define a buffered channel that can store integer types
    // The buffer size is 2
    ch := make(chan int, 2)

    // Because ch is a buffered channel, we can send two data at the same time
    // You don't need to read data synchronously immediately
    ch <- 1
    ch <- 2

    // Get these two data
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}
/*
Output results:
1
2
*/

Traverse channel and close channel
Closing the channel will not lose the data in it, but it will not block after reading the channel data and wait for new data to be written

package main

import (
        "fmt"
)

func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i < n; i++ {
                c <- x
                x, y = y, x+y
        }
        close(c)
}

func main() {
        c := make(chan int, 10)
        go fibonacci(cap(c), c)
        /*
        	range Function traverses each data received from the channel, because c closes the channel after sending 10 data, so here our range function ends after receiving 10 data.
        	If the above c channel is not closed, the range function will not end, so it will be blocked when receiving the 11th data.
        */
        for i := range c {
                fmt.Println(i)
        }
}
/*
Output results:
0
1
1
2
3
5
8
13
21
34
*/

Tags: Go Back-end

Posted on Fri, 12 Nov 2021 03:47:04 -0500 by palito