Introduction to Go language

Install Go environment

Installation package download address: https://golang.org/dl/ , it can't be opened https://studygolang.com/dl Download.

After the download and installation is completed, you can open Win+R and enter cmd to check whether the installation is successful. If you enter go version successfully and display the version number, the installation is successful

Install integrated development environment

LiteIDE download address: http://liteide.org/cn/
You can use LiteIDE, the integrated development environment of Go, or IntelliJ Idea to install the Go plug-in.
Plug in installation path: File - > Settings

elements of grammar

package

Package declaration

The first line of each go file declares which package the file belongs to. The package name defines the package name of the file. The main package represents an independent and runnable Go program. Each go application contains a package called main.

package main

Import of packages

Use the import keyword + package name to import the dependent package. The package name uses double quotation marks.

import "fmt"
import "math"

You can also import packages in parentheses

import (
	"fmt"
 	"math"
)

Export name

In go, you can only use the exported functions in the module. The exported functions begin with uppercase letters. Methods that begin with lowercase letters can only be used within a package.

package main
import ("fmt"
	"math"
)
func main() {
	fmt.Printf("%g", math.Sqrt(2))
}

package main
import ("fmt"
	"math"
)
func main() {
	fmt.Printf("%g", math.sqrt(2))
}

function

Function definition

The functions in go are defined with the func keyword. Note that the parameter type is after the parameter name, and the format is as follows

func Function name(Parameter name 1 parameter type, Parameter name 2 parameter type) Return value {

}
package main
import (
	"fmt"
)
func main() {
	fmt.Printf("3 + 5 = %d", add(3, 5))
}
func add(a int, b int) int {
	return a + b
}

Parameter list simplification

When two or more parameter types are the same, only the last parameter type can be reserved. As above, the function add can be simplified to

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

Multiple return values

Functions in go can return any number of return values

package main
import (
	"fmt"
)
func main() {
	a, b := add(3, 5)
	fmt.Printf("3 + 5 = %d, 3 - 5 = %d", a, b)
}
func add(a, b int) (int, int) {
	return a + b, a - b
}

Name of return value

The return value can be named directly. The return statement without parameters returns the named return value

func add(a, b int) (sum, difference int) {
	sum = a + b
	difference = a - b
	return
}

variable

Variables are defined with the var keyword, and the scope can be packages or functions.

var flag bool
func main() {
	var i int
	flag = true
	i = 5
	fmt.Println(i, flag)
}

Initialization of variables

A variable can be initialized at the time of declaration. If the value of the variable is initialized at the time of declaration, the type can be omitted, and the variable can obtain the type from the value.

var flag = true
func main() {
	var i = 5
	fmt.Println(i, flag)
}

Short assignment statement

In the function: = can replace the var declaration in the variable assignment with explicit type, but it cannot be used outside the function

func main() {
	i := 5
	fmt.Println(i, flag)
}

Basic data type

The basic data types in go can also be divided into three categories: integer, floating point and Boolean

integer

The data types of integers include int8, int16, int32, int64, uint8, uint16, uint32, and uint64. The integers beginning with int are signed integers, and the bits beginning with unit are unsigned integers

float

The data types of floating-point type include float32, float64, complex64, complex128, uint8, uint16, uint32 and uint64, where the integer starting with int is signed and the bit unsigned integer starting with unit is unsigned.
float is used to represent floating-point data, and complex table is a complex type

Boolean type

Boolean values can only be constants true or false

Other numeric types

Other numeric types include byte and run.
byte is equivalent to int8 and is often used to process ascii characters
rune is equivalent to int32 and is commonly used to handle unicode or utf-8 characters

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.

//The alias of int32 is equivalent to int32 in almost all aspects
//It is used to distinguish between character values and integer values

type rune = int32

Zero value

Variables that are not assigned a value are set to zero
Digital: 0
Boolean: false
String: ''

package main
import (
	"fmt"
)
var (
	flag bool
	MaxInt uint64
	str string
)
func main() {
	fmt.Printf("Type: %T Value: %v\n", flag, flag)
	fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
	fmt.Printf("Type: %T Value: %v\n", str, str)
}

Branching structure

There is only one loop structure in go language: for

for

Compared with other languages, the parentheses of for in go can be omitted

	sum := 0
	for i:= 1; i < 100; i ++ {
		sum += i
	}
	fmt.Printf("sum = %v", sum)

In addition to conditional expressions, initialization statements and post statements are optional

	sum := 1
	for sum < 10 {
		sum += sum
	}
	fmt.Printf("sum = %v", sum)

An infinite loop statement can be regarded as a for statement without a conditional expression

	for {

	}

if

The if statement is similar to the for statement. Parentheses can be omitted, but braces cannot

	flag := true
	if flag {
		fmt.Printf("true")
	} else {
		fmt.Printf("false")
	}

If can make some initialization statements before determining the conditional expression, and the scope of the statement is only valid in the if statement

import (
	"fmt"
	"math"
)
func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}
func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

switch

The case statements of switch in go are executed sequentially from top to bottom until the matching is successful. Case penetration will not occur, and only the first case that meets the conditions will be executed instead of the following cases

import (
	"fmt"
	"runtime"
)
func main() {
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	case "windows":
		fmt.Println("windows")
	default:
		fmt.Printf("NULL")
	}
}

defer

The statement decorated with defer will be pushed to the call stack and executed after the outer function returns. If multiple statements are decorated with defer, follow the order of last in first out

	fmt.Println("counting")
	for i := 0; i < 10; i++ {
		defer fmt.Println(i)
	}
	fmt.Println("done")

Pointer

There are pointers in go, but pointer calculation is not allowed. The * type declares this type of pointer, and the zero value is < nil >.
Use the & operator to generate a pointer to the specified operand.

func main() {
	i := 1
	var p *int
	fmt.Println(p)
	p = &i//Defines the pointer p to the variable i
	fmt.Println(p)//Storage address of variable i in memory
	fmt.Println(*p)
	*p = *p + 1
	fmt.Println(*p)
}

structural morphology

A struct is a set of fields that can be used Number to access the fields in the structure

type Coordinate struct {
	X int
	Y int
}
func main() {
	v := Coordinate{1, 2}
	fmt.Println(v.X)
}

You can use structure pointers to access data, or you can implicitly refer to it indirectly

type Coordinate struct {
	X int
	Y int
}
func main() {
	var p *Coordinate
	p = &Coordinate{1, 2}
	fmt.Println((*p).X)//Access through structure pointer
	fmt.Println(p.X)//Implicit indirect access
}

You can use the method of listing field values to allocate the structure. The field without assigned value is the zero value of this type

type Coordinate struct {
	X int
	Y int
}
func main() {
	var p *Coordinate
	p = &Coordinate{X:1}
	fmt.Println((*p).X)
	fmt.Println(p.Y)
}

array

go uses [] type to declare array

func main() {
	a := [5]int{1,2,3,4,5}
	fmt.Println(a)
}

section

The size of the array is fixed. Slicing provides dynamic size and flexible perspective for array elements.
Slices are defined by two subscripts, an upper bound and a lower bound, separated by colons:
Include lower bound but not upper bound

func main() {
	a := [5]int{1,2,3,4,5}
	var p = a[0:4]
	fmt.Println(p)
}

a[0:4] indicates the elements with subscripts 0 to 3 in the a array. The slice does not really store data, but only describes a piece of data in the array. Modifying the slice will modify the corresponding elements in the array.

func main() {
	a := [5]int{1,2,3,4,5}
	var p = a[0:3]
	var q = a[1:4]
	fmt.Println(p)
	fmt.Println(q)
	p[2] = 9
	fmt.Println(p)
	fmt.Println(q)
}


Slice can omit the upper and lower bounds. The default value of the lower bound is 0, and the default value of the upper bound is the length of the array
For array a [10]int, the following slice is equivalent

a[0:10]
a[:10]
a[0:]
a[:]

The length of the slice refers to the number of elements stored, and the capacity refers to the number of elements from the first element of the slice to the last element of the underlying array.

func main() {
	s := []int{1, 3, 5, 7, 9, 11}
	printSlice(s)
	// Cut the slice so that its length is 0
	s = s[:0]
	printSlice(s)
	// Extended length
	s = s[:4]
	printSlice(s)
	// Discard the first two values
	s = s[2:]
	printSlice(s)
	// Extended length
	s = s[:4]
	printSlice(s)
}
func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

The slice has a zero value of nil and no underlying array

func main() {
	var s []int
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")
	}
}

Slices can be created with the built-in function make. In case of two parameters, the second parameter is length and capacity. In case of three parameters, the second parameter is length and the third parameter is capacity

func main() {
	a := make([]int, 5)
	fmt.Println(len(a), cap(a))
	s := make([]int, 0, 5)
	fmt.Println(len(s), cap(s))
}


You can append elements to the slice through append. If the capacity of the underlying array is insufficient, a new array will be created and the slice will point to the new array.

func main() {
	var language = []string{"Java", "C", "Python"}
	fmt.Printf("first element address:%v, len:%d, cap:%d, %v\n",
	 &language[0], len(language), cap(language), language)
	language = append(language, "Golang", "Scala", "Lua")
	fmt.Printf("first element address:%v, len:%d, cap:%d, %v\n",
	 &language[0], len(language), cap(language), language)
}


The range form of the for loop can traverse slices or maps. When the for loop is used to iterate through slices, two values are returned for each iteration. The first value is the subscript of the current element, and the second value is a copy of the element corresponding to the subscript.

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
	for i, v := range pow {
		fmt.Printf("2**%d = %d\n", i, v)
	}
}


range can also assign subscripts or values to_ To ignore it. When only index is needed, the second variable can be ignored

func main() {
	pow := make([]int, 10)
	for i := range pow {
		pow[i] = 1 << uint(i) // == 2**i
	}
	for _, value := range pow {
		fmt.Printf("%d\n", value)
	}
}

mapping

Map maps keys to values. The zero value of the map is nil. Nil mapping has no keys and cannot add keys.
The make function returns a map of the given type and initializes it for standby.

type Person struct {
	Weight, Height float64
}
var m map[string]Person
func main() {
	m = make(map[string]Person)
	m["Bill"] = Person{
		70, 180,
	}
	fmt.Println(m["Bill"])
}

If the mapped element types are the same, you can declare the type only in the outer layer

type Person struct {
	Weight, Height float64
}
var m map[string]Person
func main() {
	m = map[string]Person{
		"Bill":{70, 180},
		"John":{85, 195},
	}
	fmt.Println(m)
}


You can get, modify and delete mapping elements, or use double assignment to detect whether a key exists

type Person struct {
	Weight, Height float64
}
var m map[string]Person
func main() {
	m = map[string]Person{
		"Bill":{70, 180},
		"John":{85, 195},
		"Mary":{55, 168},
	}
	fmt.Println(m)
	fmt.Println(m["John"])
	m["Bill"] = Person{75, 180}
	fmt.Println(m)
	delete(m, "John")
	fmt.Println(m)
	param1, ok := m["Jack"]
	fmt.Println(param1, ok)
	param2, ok := m["Bill"]
	fmt.Println(param2, ok)
}

go process

Go provides a lightweight thread goroutine to execute functions, using the go function name.

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")
}

channel

A channel is a pipe with a type. Create a channel through the make and chan keywords, and send or receive values through the channel operator < -.
The channel can be buffered. Provide the buffer length as the second parameter to make to initialize a buffered channel,
Blocking occurs only when the channel's buffer is full and data is sent to it. When the buffer is empty, the receiver blocks.

import "fmt"
func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum 
}
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 
	fmt.Println(x, y, x+y)
}


If the above code is changed to

func main() {
	s := []int{7, 2, 8, -9, 4, 0}
	c := make(chan int, 1)
	sum(s[:len(s)/2], c)
	sum(s[len(s)/2:], c)
	x, y := <-c, <-c
	fmt.Println(x, y, x+y)
}

When the buffer is added and the go process is removed, a lock conflict occurs

Select

The select statement enables a Go procedure to wait for multiple communication operations. Select will block until a branch can continue to execute, and then the branch will be executed. When multiple branches are ready, a random one is selected for execution.
When none of the other branches in the select are ready, the default branch is executed. To avoid blocking when trying to send or receive, use the default branch:

import (
	"fmt"
	"time"
)
func main() {
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(500 * time.Millisecond)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		default:
			fmt.Println("    .")
			time.Sleep(50 * time.Millisecond)
		}
	}
}

Mutex

Sync is available in the Go standard library Mutex mutex type and its two methods: Lock Unlock
The Lock method can be invoked before the code to invoke the Unlock method after the code to ensure the mutex execution of a piece of code.

type SafeCounter struct {
	v   map[string]int
	mux sync.Mutex
}
// Inc increases the value of the counter for a given key.
func (c *SafeCounter) Inc(key string) {
	c.mux.Lock()
	c.v[key]++
	c.mux.Unlock()
}
// Value returns the current value of the counter for a given key.
func (c *SafeCounter) Value(key string) int {
	c.mux.Lock()
	defer c.mux.Unlock()
	return c.v[key]
}
func main() {
	c := SafeCounter{v: make(map[string]int)}
	for i := 0; i < 100; i++ {
		go c.Inc("key")
	}
	fmt.Println(c.Value("key"))
}

Keywords: Go

Added by ritter on Tue, 01 Feb 2022 09:42:26 +0200