Getting Started with Basic Grammar of Go Language

Articles and codes have been archived in [Github warehouse: https://github.com/timerring/backend-tutorial ] or the public account [AIShareLab] can also be obtained by replying to go .

Introduction

What is the Go language

  1. High performance and high concurrency
  2. Simple syntax and gentle learning curve
  3. Rich standard library (in many cases, the development of basic functions can be completed without the help of third-party libraries)
  4. Perfect tool chain (whether it is compilation, code inspection, supplementary prompts, etc., there are corresponding tools, and a complete unit testing framework, performance testing, etc. are built in)
  5. Static linking (all compilation results are statically linked, you only need to copy the compiled executable file, you can run it directly without adding anything, the deployment is convenient and fast. Compared with java, it needs a huge jre to run)
  6. very fast compilation
  7. Cross-platform (can run on a variety of devices, and has cross-compilation features, no need to configure a cross-compilation environment)
  8. Garbage collection (focus on business logic, no need to consider memory allocation and release)

Which companies use the Go language

First of all, ByteDance has fully embraced the go language. There are tens of thousands of microservices in the company written in golang. Not long ago, the GORPC framework KiteX was also open sourced. Tencent, Baidu, Meituan, Didi, Sangfor, Ping An, OPPO, Zhihu, Qunar, 360, Jinshan, Weibo, bilibili, Qiniu, PingCAP and other companies also use the Go language extensively.
Foreign companies such as Google and Facebook are also using the Go language extensively.
From the perspective of business, language has been flourishing in cloud computing, microservices, big data, blockchain, Internet of Things and other fields. Then, in the fields of cloud computing and microservices,
Docker, Kubernetes, Istio, etcd, and prometheus already have a very high market share. Almost all cloud-native components are implemented in Go.

Why Transform Go Language

  • At the beginning, the company's back-end business was mainly web back-end. The early team did not have a Java background, and C++ was not suitable for online Web business.
  • The initial service was python. Since 2014, with the growth of business volume, python has encountered some performance problems.
  • Some teams initially tried to use Go and found that getting started is easy, development efficiency is high, and performance is relatively good.
  • The development and deployment of the Go language is very simple , and by the way, it solves the troublesome dependency library version problem caused by python before.
  • —After some businesses tasted the sweetness, they began to vigorously promote it at the company level, and the company's internal golang-based rpc and http frameworks were born.

Getting Started with Go

go installation

Visit go.dev/ or visit studygolang.com/dl

Go development environment

  • Cloud-based development environment: gitpods.IO online programming environment, requires GitHub account
  • Integrated development environment: Vs Code (free) or Goland (paid)

basic grammar

//  package main 代表这个文件属于main包的一部分, main包也就是程序的入口包。
package main
// 导入了标准库里面的 FMT 包。这个包主要是用来往屏幕输入输出字符串、格式化字符串。
import (
    "fmt"
)
// import 下面是 main 函数,main 函数的话里面调用了 fmt.Println 输出 helloworld要运行这个程序的话,我们就直接 go run heloworld.go。

func main() {
    
    
    fmt.Println("hello world")
}

If you want to compile it into binary, you can go buildcompile it in . After the compilation is completed, ./hellowordyou can directly run it in FMT. There are many functions in the package to do different input and output formatting tasks. You can hover the mouse over your code in the editor to see the documentation for each function.

You can also enter pkg.Go.Dev, add your package name, such as FMT, and then you can see the online documentation of this package, from which you can select the functions you need to use.

variable

package main

import (
    "fmt"
    "math"
)

func main() {
    
    
    var a = "initial"
    var b, c int = 1, 2
    var d = true
    var e float64

    f := float32(e)
    g := a + "foo"
    fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
    fmt.Println(g)                // initialapple

    const s string = "constant"
    const h = 500000000
    const i = 3e20 / h
    fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}

Let's look at the second example, about variable types.

The go language is a strongly typed language, and each variable has its own variable type. Common variable types include strings, integers, floating-point types, Boolean types, etc. Strings in the go language are built-in types, which can be directly concatenated with a plus sign, or directly used to compare two strings with an equal sign.

Declaration of variables in go language, there are two ways to declare variables in go language

  • One is var name string=" "to declare variables in this way. When declaring a variable, the type of the variable is generally automatically deduced. If necessary, the variable type can also be written out explicitly.
  • Another way to declare variables is to use 变量 := 等于值.

For constants, change var to const. The value mentioned is a constant in the go language. It has no definite type, and the type will be automatically determined according to the context of use.

if-else

package main

import "fmt"

func main() {
    
    

	if 7%2 == 0 {
    
    
		fmt.Println("7 is even")
	} else {
    
    
		fmt.Println("7 is odd")
	}

	if 8%4 == 0 {
    
    
		fmt.Println("8 is divisible by 4")
	}

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

The writing method of if else in Go language is similar to C or C++. The difference is that there are no parentheses after if, and if in Golang, it must be followed by curly braces, and you cannot directly put the statements in if on the same line like C or C++.

cycle

package main

import "fmt"

func main() {
    
    

	i := 1
	for {
    
    
		fmt.Println("loop")
		break
	}
	for j := 7; j < 9; j++ {
    
    
		fmt.Println(j)
	}

	for n := 0; n < 5; n++ {
    
    
		if n%2 == 0 {
    
    
			continue
		}
		fmt.Println(n)
	}
	for i <= 3 {
    
    
		fmt.Println(i)
		i = i + 1
	}
}

There are no while loops and do while loops in go, but only one kind of for loop. The simplest for loop is to write nothing after for, which means an infinite loop. You can use break or continue to break out or continue the loop.

switch

package main

import (
	"fmt"
	"time"
)

func main() {
    
    

	a := 2
	switch a {
    
    
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {
    
    
	//  case 里面写条件分支
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}

The variable name behind the switch does not require parentheses. There is a big difference here. In C++, if the switch case does not display a break, it will continue to run through all the cases. In the go language, there is no need to add a break.
Compared with C or C++, the switch function in go language is more powerful. Arbitrary variable types can be used and can even be used to replace arbitrary if else statements. You can not add any variables behind the switch, and then write conditional branches in the case. In this way, the code logic will be clearer than if you use multiple if else codes.

array

package main

import "fmt"

func main() {
    
    

	// 这里的话是一个可以存放 5 个 int 元素的数组 A
	var a [5]int
	a[4] = 100
	fmt.Println("get:", a[2])
	fmt.Println("len:", len(a))

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

	var twoD [2][3]int
	for i := 0; i < 2; i++ {
    
    
		for j := 0; j < 3; j++ {
    
    
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d: ", twoD)
}

An array is a numbered sequence of elements of fixed length. For an array, it is very convenient to get the value of a specific index or store a value at a specific index, and then directly print an array. However, in real business code, we rarely use arrays directly, because their length is fixed, and we use more slices.

slice

package main

import "fmt"

func main() {
    
    

	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[2])   // c
	fmt.Println("len:", len(s)) // 3

	s = append(s, "d")
	s = append(s, "e", "f")
	fmt.Println(s) // [a b c d e f]

	c := make([]string, len(s))
	copy(c, s)
	fmt.Println(c) // [a b c d e f]

	fmt.Println(s[2:5]) // [c d e]
	fmt.Println(s[:5])  // [a b c d e]
	fmt.Println(s[2:])  // [c d e f]

	good := []string{
    
    "g", "o", "o", "d"}
	fmt.Println(good) // [g o o d]
}

Unlike arrays, slices can change their length arbitrarily, and have more operations. You can use make to create a slice, you can get values ​​like an array, and use append to add elements.

Pay attention to the usage of append, you must assign the result of append to the original array.
Because the principle of slice is actually that it has a length and a capacity, plus a pointer to an array, when you perform the append operation, if the capacity is not enough, it will expand and return a new slice.

Slice can also specify the length when it is initialized.
Slice has the same slicing operation as python, for example, this means to take out the elements from the second to the fifth position, excluding the fifth element. But unlike python, negative indexes are not supported here .

map

package main

import "fmt"

func main() {
    
    
	// 用 make 来创建一个空 map,这里会需要两个类型。
	// 第一个是那个 key 的类型,这里是 string 另一个是 value 的类型,这里是 int。
	m := make(map[string]int)
	// 可以从里面去存储或者取出键值对。
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m)           // map[one:1 two:2]
	fmt.Println(len(m))      // 2
	fmt.Println(m["one"])    // 1
	fmt.Println(m["unknow"]) // 0

	r, ok := m["unknow"]
	fmt.Println(r, ok) // 0 false

	// 可以用 delete 从里面删除键值对。
	delete(m, "one")

	m2 := map[string]int{
    
    "one": 1, "two": 2}
	var m3 = map[string]int{
    
    "one": 1, "two": 2}
	fmt.Println(m2, m3)
}

Map is the most frequently used data structure in actual use.
Golang's map is completely unordered. When traversing, it will not be in alphabetical order, nor will it be output in the order of insertion, but in random order.

range

package main

import "fmt"

func main() {
    
    
	nums := []int{
    
    2, 3, 4}
	sum := 0
	for i, num := range nums {
    
    
		sum += num
		if num == 2 {
    
    
			fmt.Println("index:", i, "num:", num) // index: 0 num: 2
		}
	}
	fmt.Println(sum) // 9

	m := map[string]string{
    
    "a": "A", "b": "B"}
	for k, v := range m {
    
    
		fmt.Println(k, v) // b 8; a A
	}
	for k := range m {
    
    
		fmt.Println("key", k) // key a; key b
	}
}

For a slice or a map, we can use range to quickly traverse, so that the code can be more concise. When Range traverses, two values ​​are returned for the array, the first is the index, and the second is the value of the corresponding position. If we don't need an index, we can omit it with an underscore.

function

package main

import "fmt"

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

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

func exists(m map[string]string, k string) (v string, ok bool) {
    
    
	v, ok = m[k]
	return v, ok
}

func main() {
    
    
	res := add(1, 2)
	fmt.Println(res) // 3
	// 第一个是真正的返回结果,第二个值是一个错误信息
	v, ok := exists(map[string]string{
    
    "a": "A"}, "a")
	fmt.Println(v, ok) // A True
}

This is a simple function in Golang to add two variables. The difference between Golang and many other languages ​​is that the variable type is postfixed.
Functions in Golang natively support returning multiple values. In the actual business logic code, almost all functions return two values, the first is the real return result, and the second value is an error message.

pointer

package main

import "fmt"

func add2(n int) {
    
    
	n += 2
}

func add2ptr(n *int) {
    
    
	*n += 2
}

func main() {
    
    
	n := 5
	add2(n)
	fmt.Println(n) // 5
	add2ptr(&n)
	fmt.Println(n) // 7
}

Pointers are also supported in Go. Of course, compared to pointers in C and C++, the supported operations are very limited. One of the main uses of pointers is to modify incoming parameters.

For example the function above tries to add 2 to a variable. But simply writing like the above is actually invalid. Because the parameter passed in to the function is actually a shell, it is also said that this +2 is +2 for the copy, and it does not work. If we need to work, then we need to write that type as a pointer type, then for type matching, an & symbol will be added when calling.

structure

package main

import "fmt"

type user struct {
    
    
	name     string
	password string
}

func main() {
    
    
	a := user{
    
    name: "wang", password: "1024"}
	b := user{
    
    "wang", "1024"}
	c := user{
    
    name: "wang"}
	c.password = "1024"
	var d user
	d.name = "wang"
	d.password = "1024"

	fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
	fmt.Println(checkPassword(a, "haha"))   // false
	fmt.Println(checkPassword2(&a, "haha")) // false
}

func checkPassword(u user, password string) bool {
    
    
	return u.password == password
}
// 同样的结构体我们也能支持指针,这样能够实现对于结构体的修改,也可以在某些情况下避免一些大结构体的拷贝开销。
func checkPassword2(u *user, password string) bool {
    
    
	return u.password == password
}

A structure is a collection of typed fields.
For example, the user structure contains two fields, name and password. We can use the name of the structure to initialize a structure variable. When constructing, we need to pass in the initial value of each field. You can also use this key-value pair to specify the initial value, so that only a part of the fields can be initialized.

Struct method

package main

import "fmt"

type user struct {
    
    
	name     string
	password string
}

func (u user) checkPassword(password string) bool {
    
    
	return u.password == password
}

func (u *user) resetPassword(password string) {
    
    
	u.password = password
}

func main() {
    
    
	a := user{
    
    name: "wang", password: "1024"}
	a.resetPassword("2048")
	fmt.Println(a.checkPassword("2048")) // true
}

In Golang, some methods can be defined for the structure. It will be a bit similar to class member functions in other languages. For example, here, we checkPasswordchanged the implementation of in the above example from an ordinary function to a structure method. In this way, the user can call it like a.checkPassword ("x ). D The specific code modification is to add the first parameter, add parentheses, and write it in front of the function name.

There are also two ways of writing when implementing the method of the structure, one is with a pointer, and the other is without a pointer. If you have a pointer, you can modify this structure. If there is no pointer, the operation is actually a copy, and you cannot modify the structure.

error handling

package main

import (
	"errors"
	"fmt"
)

type user struct {
    
    
	name     string
	password string
}
// 在函数里面,我们可以在那个函数的返回值类型里面,后面加一个 error,就代表这个函数可能会返回错误。
func findUser(users []user, name string) (v *user, err error) {
    
    
	for _, u := range users {
    
    
		if u.name == name {
    
    
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

func main() {
    
    
	u, err := findUser([]user{
    
    {
    
    "wang", "1024"}}, "wang")
	if err != nil {
    
    
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) // wang

	if u, err := findUser([]user{
    
    {
    
    "wang", "1024"}}, "li"); err != nil {
    
    
		fmt.Println(err) // not found
		return
	} else {
    
    
		fmt.Println(u.name)
	}
}

Error handling in the go language conforms to the idiomatic approach of using a separate return value to convey error information.

It is different from the exceptions used by Java itself. The processing method of the Go language can clearly know which function returns an error, and can use a simple if else to handle the error.
In a function, we can add an error to the return value type of that function, which means that the function may return an error.
Then when the function is implemented, return needs to return two values ​​at the same time, or if an error occurs, it can be return nilcombined with one error. If not, then return the original result and nil.

string manipulation

package main

import (
	"fmt"
	"strings"
)

func main() {
    
    
	a := "hello"
	fmt.Println(strings.Contains(a, "ll"))                // true
	fmt.Println(strings.Count(a, "l"))                    // 2
	fmt.Println(strings.HasPrefix(a, "he"))               // true
	fmt.Println(strings.HasSuffix(a, "llo"))              // true
	fmt.Println(strings.Index(a, "ll"))                   // 2
	fmt.Println(strings.Join([]string{
    
    "he", "llo"}, "-")) // he-llo
	fmt.Println(strings.Repeat(a, 2))                     // hellohello
	fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo
	fmt.Println(strings.Split("a-b-c", "-"))              // [a b c]
	fmt.Println(strings.ToLower(a))                       // hello
	fmt.Println(strings.ToUpper(a))                       // HELLO
	fmt.Println(len(a))                                   // 5
	b := "你好"
	fmt.Println(len(b)) // 6
}

There are many commonly used string tool functions in the strings package of the standard library, such as contains to determine whether a string contains another string, count to count strings, and index to find the position of a certain string. Join joins multiple strings repeat repeats multiple strings replace replaces strings.

string formatting

package main

import "fmt"

type point struct {
    
    
	x, y int
}

func main() {
    
    
	s := "hello"
	n := 123
	p := point{
    
    1, 2}
	fmt.Println(s, n) // hello 123
	fmt.Println(p)    // {1 2}

	fmt.Printf("s=%v\n", s)  // s=hello
	fmt.Printf("n=%v\n", n)  // n=123
	fmt.Printf("p=%v\n", p)  // p={1 2}
	fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}

	f := 3.141592653
	fmt.Println(f)          // 3.141592653
	fmt.Printf("%.2f\n", f) // 3.14
}

In the go language, you can easily use it %vto print any type of variable without distinguishing between numeric strings. You can also %+vprint verbose results with , %#vwhich is more verbose.

JSON processing

package main

import (
	"encoding/json"
	"fmt"
)

type userInfo struct {
    
    
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
    
    
	a := userInfo{
    
    Name: "wang", Age: 18, Hobby: []string{
    
    "Golang", "TypeScript"}}
	buf, err := json.Marshal(a)
	if err != nil {
    
    
		panic(err)
	}
	fmt.Println(buf)         // [123 34 78 97...]
	fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

	buf, err = json.MarshalIndent(a, "", "\t")
	if err != nil {
    
    
		panic(err)
	}
	fmt.Println(string(buf))

	var b userInfo
	err = json.Unmarshal(buf, &b)
	if err != nil {
    
    
		panic(err)
	}
	fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}

The JSON operation in the go language is very simple. For an existing structure, we can do nothing, as long as the first letter of each field is uppercase, that is, it is a public field. Then this structure can be deserialized with JSON.Marshaler and turned into a JSON string.
The serialized string can also be deserialized into an empty variable with JSON.Unmarshaler.
If the string serialized by default in this way, its style starts with an uppercase letter instead of an underscore. We can use syntax such as json tag to modify the field name in the output JSON result later.

time processing

package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	now := time.Now()
	fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
	t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
	t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
	fmt.Println(t)                                                  // 2022-03-27 01:25:36 +0000 UTC
	fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
	fmt.Println(t.Format("2006-01-02 15:04:05"))                    // 2022-03-27 01:25:36
	diff := t2.Sub(t)
	fmt.Println(diff)                           // 1h5m0s
	fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
	t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
	if err != nil {
    
    
		panic(err)
	}
	fmt.Println(t3 == t)    // true
	fmt.Println(now.Unix()) // 1648738080
}

The following is time processing. The most commonly used in the go language is time.Now ()to obtain the current time, and then you can also use time.Dateto construct a time with a time zone, and the constructed time. There are many methods above to obtain the year, month, day, hour, minute, and second at this point in time, and then you can also use sub to subtract the two times to get a time period. In the time period, you can get how many hours, how many minutes, and how many seconds it has.

When interacting with certain systems, we often use timestamps. Then you can use .UNIX()to get the timestamp. time.format, time.parseused to parse the time, please refer to the documentation for details.

digital analysis

package main

import (
	"fmt"
	"strconv"
)

func main() {
    
    
	f, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(f) // 1.234

	n, _ := strconv.ParseInt("111", 10, 64)
	fmt.Println(n) // 111

	n, _ = strconv.ParseInt("0x1000", 0, 64)
	fmt.Println(n) // 4096

	n2, _ := strconv.Atoi("123")
	fmt.Println(n2) // 123

	n2, err := strconv.Atoi("AAA")
	fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}

Let's learn how to convert between strings and numbers. In the go language, all the conversions between strings and numbers are under strconvthis package, which is the abbreviation of the two words string convert.
We can use parselnt or parseFloat to parse a string. Parseint parameter
We can use Atoi to convert a decimal string into a number. You can use itoA to convert numbers into strings. If the input is invalid, these functions will return error.

process information

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
    
    
	// 比如我们编译的一个二进制文件,command。后面接 abcd 来启动,输出就是os.argv会是一个长度为5的 slice ,第一个成员代表:进制自身的名字。
	// go run example/20-env/main.go a b c d
	fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
	fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
	fmt.Println(os.Setenv("AA", "BB"))

	buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
	if err != nil {
    
    
		panic(err)
	}
	fmt.Println(string(buf)) // 127.0.0.1       localhost
}

In go, we can os.argvuse get the specified command line parameters when the program is executed.
We can os.getenvuse read environment variables.

Guess you like

Origin blog.csdn.net/m0_52316372/article/details/131928615