Basic usage of Go(1)

Basic usage of Go(1)

Author: Once Day Date: January 8, 2023

It's been a long road, has anyone ever smiled at you...

Reference documents:

1 Overview

The Go language was written by a group of bigwigs from Google, namely Robert Griesemer, Rob Pike, and Ken Thompson.

The Go language is suitable for building infrastructure software, such as web servers, tools and systems used by programmers, and so on.

Go is an open source project, so the source code for its compilers, libraries, and tools is freely available to everyone.

The Go language is a C-like language that inherits expression syntax, control flow statements, basic data types, call-by-value parameter passing, and pointers.

The Go language was inspired by CSP (Communication Sequential Process, Communicating Sequential Process).

The core feature of the Go language is simplicity. It has many basic features, such as garbage collection, package system, first-class citizen functions, lexical scope, system call interface, etc., but complex features such as overloading, generics, and classes, etc. too will increase.

There are many ways to install Go, and it is very simple to install under Linux (root privileges):

apt install golang-go

For installation on other platforms, please refer to:

After the installation is complete, enter the following command on the command line to view:

go version 	//查看版本
go help		//查看帮助

The go version used is preferably 1.5above:

onceday->~:# go version
go version go1.18.1 linux/amd64

2. Basic use

The development environment uses Tencent Cloud Linux server, and uses vscode remote SSH connection for development (the specific process can be done by Baidu).

Vscode needs to install gosupport plug-ins, you can search directly in the plug-in market, you may be prompted to install supporting support packages, just agree.

You can refer to the following documents:

The proxy needs to be configured :

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

Note that if the go plugin of vscode keeps prompting to install the supporting support package, then the proxy configuration above may not be successful, you need to reconfigure the proxy, then restart vscode, and try again .

First, let's output a classic hello world:

package main

import "fmt"

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

Then run the command:

ubuntu->go-code:$ go run helloworld.go 
hello, world!

Go supports natively Unicode, so all national languages ​​can be handled.

It can be further compiled into a binary program:

ubuntu->go-code:$ go build helloworld.go 
ubuntu->go-code:$ ll
total 1732
drwxrwxr-x  2 ubuntu ubuntu    4096 Jan  8 23:31 ./
drwxr-x--- 11 ubuntu ubuntu    4096 Jan  8 23:08 ../
-rwxrwxr-x  1 ubuntu ubuntu 1758417 Jan  8 23:42 helloworld*
-rw-rw-r--  1 ubuntu ubuntu      74 Jan  8 23:29 helloworld.go

Use file to view the specific information of the file:

ubuntu->go-code:$ file helloworld
helloworld: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=z9i3RzrVufV2SEsr681w/mhtdOnbVF3mshdHAs_fP/rfCmQ8VmCzmj-XWabnsZ/mBuD7qqCOWorc7GsomZ5, not stripped

gobuild, which indicates that it is an executable program built in the Go language.

3. Variables and declarations

The basic composition of a Go program has the following parts: package declaration, import package, function, variable, statement/expression, comment.

Except for comments, all kinds of naming follow a simple rule: the beginning of the name is a letter (a character in Unicode) or an underscore, followed by any number of characters, numbers and underscores, and is case-sensitive .

GO has 25 keywords, as follows, they cannot be used as names:

break,default,func,interface,select,case,defer,go,map,struct,chan,else,goto,package,switch,const,fallthrough,if,range,type,continue,for,import,return,var

There are also more than thirty built-in predeclared constants, types, and functions:

Constants: true, false, iota, nil

类型:int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、uintptr、float32、float64、complex128、complex64、bool、byte、rune、string、error

函数:make、len、cap、new、append、copy、close、delete、complex、real、imag、panic、recover

Predeclared names are overridable. But this could create a risk of conflict.

For variables with local scope, Go language is accustomed to using short commands.

For the names of word combinations, the Go language uses the camel-case style and does not use underscores very much.

3.1 Declaration and scope

If an entity is declared in a function, it is only valid locally in the function, and if it is declared outside the function, it will be visible to all source files in the package.

The case of the first letter of an entity determines its visibility across packages. If the name starts with a capital letter, it is exported, meaning it is visible and accessible outside the package . Package names are always composed of lowercase letters.

GO programs are stored in one or more files with the suffix .go, and each file starts with a package declaration, indicating which package the file belongs to.

The types, variables, constants, functions, etc. of the GO language are declared in no particular order, so they are globally visible in the source file.

Local declarations are only visible within the function in which they are declared, and may be visible to a small area within the function .

A function declaration contains a name, a parameter list (variables provided by the function's caller), an optional return value list, and the function body. If the function returns nothing, the return list can be ignored.

func main () {
    
    
	var a = 1
	return a
}
3.2 Variable declaration

The var declaration creates a variable of a concrete type, then attaches a name to it, and sets its initial value. The general form is as follows:

var name type = expression

Only one of type and expression can be omitted, not both, and the variable type will be specified by both .

Unassigned variables are initialized to zero by default:

  • The numeric type is 0.
  • Boolean type is false.
  • The string is "".
  • Interface and reference types (slice, pointer, map, channel, function), etc. are nil.
  • For compound data structures, the zero value is the zero value of all members.

Note that uninitialized variables do not exist in Go .

Package-level variables are initialized before main starts, and local variable initialization and declaration processes are performed during function execution .

Local variables can be declared and initialized using the short variable declaration form .

name := expression

Multiple variables can be declared and initialized in short variable declarations:

i, j := 0, 1

It needs to be distinguished from the following multiple assignments:

i, j = j, i

Here is an example of a short variable declaration:

f, err := os.Open(name)

A short variable declaration does not need to declare all the variables on the left. If a variable has been declared, then for this variable, the segment declaration is equivalent to assignment .

in, err := os.Open(in_name)
out, err := os.Open(out_name)
in, err := os.Open(in_name2)

As shown in the above three lines of code, the third one will have an error and cannot be compiled, because the short variable declaration needs to declare at least one variable.

Note that only variables in the same lexical block will take effect, and outer declarations will be ignored .

3.3 Pointers

The value of a pointer is the address of a variable, and a pointer indicates where the value is stored. All variables have addresses, but not all values ​​have addresses . as follows:

x := 1
p := &x // p 是整型指针,指向x

Use the pointer as follows:

fmt.Println(*p)

Pointers can be compared and are only safe if they point to the same variable or both are nil .

Unlike the C language, it is safe for a function to return the address of a local variable, but the address returned is different for each call.

func f() *int {
    
    
	v := 1
	return &v
}

For the functions above, each call returns a new address .

The Go language has a garbage collection function, so the use of variables is automatically marked, and a new mark is created every time the address of a variable is used or a pointer is copied .

3.4 new function

The expression new(type) creates an unnamed variable of type type, initializes it to a zero value of type T, and returns its address (address type is *T).

p := new(int)
fmt.Println(*p)

For some special types, for example struct {}, the new function returns the same address.

The new function is a pre-declared function, not a keyword, so it can be overwritten and declared .

3.5 Variable declaration cycle

The lifetime of a package-level variable is the execution time of the entire program. Local variables have a dynamic life cycle: a new entity is created each time the declaration statement is executed, and the variable lives until it becomes inaccessible, at which point the space it occupies is reclaimed.

If a local variable uses a pointer to expand its scope, it may change from a stack variable to a heap variable, which will bring additional memory burden.

For variables with a long life cycle, if the variables with a short life cycle are kept inside, the garbage collector will be organized to reclaim the object space with a short life cycle.

3.6 Assignment

In addition to the basic =assignment, it is only a combination of assignment symbols, such as *=, +=and so on. v++It also includes v--operations such as self-increment and self-decrement.

Multiple assignment allows several variables to be assigned at once:

x, y = y, x

The above expression swaps the values ​​between xand .y

Generally, multiple assignment is used for functions that receive and return multiple values, as follows :

f, err = os.Open("foo.txt")

If two values ​​can be compared, they need to be assignable.

3.7 Type declarations

A type declaration can be used to define a new named type that uses the same underlying type as an existing type.

type name underlying-type

The underlying type of a named type determines its structure and expressions, as well as the set of supported internal operations that are the same as if the underlying type were used directly.

Values ​​of different named types are not directly comparable .

3.8 Packages and files

Each package provides a separate namespace for its declarations. All publicly visible identifiers in Go begin with a capital letter .

In a Go program, each package is identified by a unique string called an import path.

import (
	"fmt"
	"os"
	"./tool/foo"
)

For the import paths above, each package also has a package name. The package name appears in the package declaration as a short name, which by convention matches the last segment of the import path .

Importing packages that are not used will trigger an error, so you need to make sure that the import is actually used.

Package initialization will be executed in a certain order, first package-level variables, which will be initialized in the order of dependencies and declarations .

A package containing multiple go files, the go tool will be initialized according to the internal sorting results, if some variables require a complicated initialization process, then initfunctions can be used.

func init() {
    
    /****/}

initFunctions cannot be called and referenced. In each file, when the program starts, the init functions are automatically executed in the order in which they are declared.

Before the main function is executed, all packages will be fully initialized .

3.9 Scope

The scope of the statement is different from the statement cycle. The scope of the general statement is the lexical block, and the most obvious one is the code segment surrounded by curly braces.

The lexical block of a declaration determines the scope of the declaration.

  • Variables declared in a global block are visible to the entire program.
  • Variables declared at the package level can be referenced by any file in the same package.
  • Imported packages are at the file level and can be referenced within the same file.
  • Local declarations are only used within corresponding braces.

When the compiler encounters a reference name, it will search for its declaration from the innermost closed lexical block. If it is not found, it will prompt. undeclared nameNote that the inner declaration will override the outer declaration .

When if/for/switchusing short statements in etc. control statements, you need to pay special attention to the scope of variables to avoid error reporting and overwriting.

  • if/forThe control body and execution body (loop body) of are lexical blocks, so we need to pay attention to their scopes respectively.
  • switchThe casestatements are also separate lexical blocks.

4. Data type

The data types of Go language are mainly divided into four types: basic type, aggregate type, reference type, and interface type. Only the first two basic types are introduced here.

4.1 Integer (basic type)
type occupied size overview
int/uint 32 bit/64 bit Determined according to (32-bit/64-bit) platform, or the most efficient size
int8/uint8 8 bits fixed size
int16/uint16 16 bits fixed size
int32/uint32 32 bit fixed size
int64/uint64 64 bit fixed size
rune 32 bit int32 type synonym, specific to unicode code points
byte 8 bits uint8 synonym, emphasizing that a value is raw byte data
uintptr 32-bit/64-bit/… can store exactly the next pointer

Signed numbers are represented using two's complement notation. For int/uint/uintptrthese types of indeterminate size, explicit conversion to other integer types is required .

Binary arithmetic operators include the following, applicable to integers, floating-point numbers, and complex numbers.

Arithmetic priority
* / % << >> & &^ 1
`+ - ^`
== != < <= > >= 3
&& 4
`

You can see that there are five types of priority operators, but the actual use is ()still the best choice .

Here are some descriptions of integers:

  • Operations at the same level satisfy the associative law, and are calculated sequentially from left to right.

  • The modulo operator %can only be used with integers, and its behavior varies by programming language. The sign of the modulo remainder is always the same as the dividend.

  • The behavior of division /is related to the data type. The quotient of integer division always discards the decimal part, and the quotient of floating-point division results in a decimal.

  • If the number of digits required for the result of an integer operation exceeds the range of this type, that is, an overflow occurs, the high-order bits of the overflow will be lost directly without any error reporting .

  • ^45Indicates bitwise inversion 12^21and XOR, that is, unary operations conform to binary operators and have different meanings .

  • &^is a bitwise clear operation, x&^y, the position corresponding to the bit is xset yto 10, and other bits remain unchanged.

  • In the shift operation, x>>nthe operand nmust be an unsigned number, and for a signed number to shift right, use the sign bit to fill the vacancy, that is, arithmetic right shift.

  • When a floating-point number is converted to a decimal, the decimal part will be discarded and truncated towards zero (integer down, negative number up).

  • Integers can be written in decimal, octal 0666, or hexadecimal 0xdeadbeef.

4.2 Floating point numbers (basic type)

The Go language uses IEEE 754 standard floating-point numbers, float32and supports float64two types. The significand of float32 is about 6, and the significand of float64 is about 15.

Numbers can be represented using scientific notation, 6.201e23, 4.123e-25.

When an erroneous operation is performed, such as x/0, an error will be reported and returned NaN, NaNwhich cannot be compared, it is always not equal to any value.

4.3 complex (basic type)

Go has two sizes of complex numbers, complex64 and complex128, constructed from float32 and float64 respectively.

var x complex128 = complex(1, 2) //1 + 2i
y := 1 + 2i

A floating-point number or a decimal integer is followed by the letter i, for example 3.14159i, it becomes an imaginary number, representing a complex number whose real part is 0.

4.4 Boolean (boolean) (basic type)

There are two kinds of values true​​and false false, the conditions in if and for statements are boolean values.

Boolean operations have short-circuit rules, if the operand on the left of the operator can directly determine the overall result, the operand on the right will not be evaluated .

A boolean cannot be directly implicitly converted to a number, nor can it use the int(b) form . Can be converted as follows:

if b {
	return 1
}
return 0
4.5 String (basic type)

Strings are immutable sequences of bytes that can contain any data, such as zero-valued bytes .

len(s)Returns the number of bytes in the string. s[i]Access the first icharacter of s. Strings start counting from 0. An error will be triggered if an out-of-bounds access is made .

Slicing operations are supported, such as s[0:5]returning [0, 5)the characters in the range (not including the right boundary), this operation will generate a new string.

s := "hello, world"
s[:5] // "hello"
s[7:] // "world"
s[:]  // "hello, world"

The plus (+) operator concatenates two strings to produce a new string.

new" + s[:6]

Strings can be compared using comparison operators, such as ==and <, the comparison operation is performed by byte, and the result is subject to its own dictionary sorting .

Strings cannot be changed, so two strings can share the same memory, and the overhead of copying strings is small .

String values ​​become string literals string literal.

The hexadecimal escape character is written as \xhha format, which can be large or small, and must be two digits. Octal is \ooo, must be three octal digits.

The written form of a native string literal is ``, that is, two backticks. Native string literals can expand to multiple lines, but carriage returns are removed and newlines are preserved, ensuring that the same string has the same value on all platforms .

For non-ASCII characters, the number of bytes occupied by a single character is greater than 1 byte . rangeSupport string traversal, can automatically identify multi-byte characters.

for i, r := range "hello, 世界" {
    
    
	xxxxxxxxxxxx;
}

There are four main packages for string manipulation:

  • strings, the basic functions for string manipulation.
  • bytes, operate byte slice type.
  • strconv, convert boolean values, integers, floating point numbers to the corresponding string form, or vice versa.
  • unicode, a function for judging the properties of literal symbol values.

In bytes, the Buffer type is provided for efficient processing of byte slices. The Buffer is empty at first, and its size grows as various types of data are written . Add UTF-8 encoding of arbitrary text symbols, the best method bytes.Bufferto use WriteRune. To append ASCII characters, use writeByte.

4.6 Constants (basic types)

Constants can calculate the value of the expression at the compile stage, and do not need to wait until runtime. as follows:

const (
	e = 2.718....
	pi = 3.14159......
)

Constant operands, that is, the results of mathematical, logical, and comparison operations are still constants. A constant declaration can specify both a type and a value. If no type is explicitly specified, the type is inferred from the expression on the right.

Constant declarations can use constant generators iota, which are common enumerationenumeration types.

const (
	Sunday = iota
	Monday
	TuesDay
	Wednesday
	Thursday
	Friday
	Saturday
)

The above starts enumeration from 0, and can also use i << iotabitwise to start enumeration.

Go has unspecified constants of dependent types, which can represent higher precision than basic types, at least up to 256 bits.

There are six types of constants whose dependent types are undetermined: untyped Boolean, untyped integer, untyped literal symbol, untyped floating-point number, untyped complex number, and untyped string.

Untyped constants can temporarily maintain higher precision and allow more expressions to be written without type conversion than typed constants.

Only constants are untyped, and assignment results in an implicit type conversion . If the variable type cannot represent the original value of the constant (rounding is allowed), the compiler will report an error.

If no type is explicitly specified in the variable declaration, the untyped variable is implicitly converted to the variable's default type .

So when converting, it is best to specify the type of conversion displayed .

4.7 Array (aggregate type)

An array has a fixed length and contains a sequence of zero or more elements of the same data type. Arrays can be accessed by index. Starting from 0 for the first element.

By default, each element of an array is initialized to the zero value of the corresponding type. You can also use the following initialization methods:

var q [3]int = [3]int{
    
    1, 2, 3}
var p [3]int = [3]int{
    
    1, 2}

The following definition method can determine the length of the array by the number of elements in the initialized array:

q := [...]int{1, 2, 3}

The length of the array is part of the array type, so [3]int and [4]int are two different array types . The length of the array must be a constant expression.

Arrays can also be initialized in a given order of elements as follows:

var p [3]int = [3]int{
    
    0:1, 2:2}

If the element types can be compared, the arrays can also be compared (==/!=), and the elements of the two arrays must be exactly the same to be equal .

Special attention, in Go, the array passed in the function parameter is passed as a value, that is, a copy is copied .

Array parameters can be passed by pointer .

func zero(ptr *[32]byte) {
    
     ... }
4.8 variable sequence slice (aggregate type)

A slice represents a variable-length sequence of elements of the same type. A slice is usually written []Tas where the types are all T. Similar to an array with no length limit.

Slice has three attributes: pointer, length, and capacity .

  • A slice will have an underlying array that actually stores the data.
  • The pointer will point to the first element of the array accessible from the slice.
  • The length is the number of elements in the value slice and cannot exceed the capacity of the slice.
  • The size of the capacity usually refers to the number of elements from the start element of the slice to the last element of the underlying array .

Go's built-in functions lenand capreturns the length and capacity of a slice . An underlying array can correspond to multiple slices.

num := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}

The above declaration is an array declaration with 9 elements, which can be used as the underlying array of slice .

The following is to create multiple slice objects based on this array:

s1 := num[0:5]
s2 := num[2:7]
......

The starting element of s1 is num[0], the length is 5, and the cap capacity is 9.

The starting element of s2 is num[2], the length is 5, and the cap capacity is 7.

If the reference of the slice exceeds the capacity of the referenced object, that is, cap(s), it will cause a program macro machine. If the reference of the slice exceeds the length of the referenced object, that is, len(s), the final slice will be longer than the slice .

A slice contains pointers to array elements, so when a slice is passed to a function, the elements of the underlying array can be modified inside the function .

The following declares a slice literal ( without specifying the length ):

s := []int{0, 1, 2, 3, 4, 5}

Slices cannot be compared, and cannot be used ==to determine whether two slices have the same elements. Therefore, slice comparison generally requires a custom function to achieve.

The only slice can be compared with nil , such as:

if summer == nil { /* ... */ }

The zero value of the slice type is nil, and the slice with the value of nil has no corresponding underlying array, and its capacity and length are both 0.

It should be noted that when the slice is not nil, the capacity and length may also be 0, that is, []int{}the summake([]int, 3)[3:] .

A type conversion expression can be used to generate a nil value of the specified type. For example, in the following three cases, the slice is all nil.

var s []int
s = nil
s = []int(nil)

A nil silce is no different from any other zero-length slice, and Go functions treat zero-length slices in the same way .

The built-in function make can be used to create a slice with a specified element type, length, and capacity, where the capacity is negligible, in which case the length and capacity of the slice are equal.

make([]T, len)
make([]T, len, cap) //等同于 make([]T, cap)[:len]

The built-in appendfunction is used to append elements to the back of the slice. If the capacity of the original underlying array is not enough, a new array with sufficient capacity will be allocated for storage, and the original elements will be copied . Therefore, it needs to be used as follows, considering the possibility that the underlying array may transform:

y = append(x, new_elememt)

For scenarios where it is possible to change the length or capacity of the slice, or make the slice point to a different underlying array, the slice variable needs to be updated .

In addition to being an indirect reference to the underlying array, a slice also contains length and capacity information, so it is an aggregate data structure.

4.9 hash table map (aggregate trype)

A hash table map is an unordered collection of elements with key-value pairs.

The value corresponding to the key can be obtained, updated, or removed through the key. No matter how large the hash table is, these operations can basically be completed through constant-time key comparison .

In the Go language, the map type is map[K]V, where K and V are the data types corresponding to the keys and values ​​of the dictionary. All keys in the map have the same data type, and all values ​​also have the same data type, but the type of the key and the type of the value are not necessarily the same .

The type of the key must be ==a data type that can be compared, such as sliceother types cannot be directly used as keys .

There are two ways to create a map:

ages := make(map[string]int)
ages := map[string]int{
    
    
    "alice": 31,
    "charlie": 34,
}

An empty map can map[string]int{}be used for representation.

Access is accessed by subscript:

ages["alice"] = 34
ages["charlie"] = 34

Use built-in functions deleteto remove an element from a dictionary by key:

delete(ages, 'alice')

For map, even if the key does not exist, it is safe to return a zero value of the corresponding type .

The address of the map generally cannot be obtained, because the growth of the map may cause the existing elements to be re-hashed to the new storage location, which may invalidate the obtained address .

You can use a for loop (combined with the range keyword) to traverse all the keys and corresponding values ​​in the map.

forname, age := range ages {
	fmt.Printf("%s\t%d\n", name, age)
}

The value of the map variable declared directly as follows is zero (nil), that is, there is no corresponding hash table:

var ages map[string]int 

Most map operations can be performed on the zero value nil of the map, including finding elements, deleting elements, getting the number of map elements (len), and executing range loops. But trying to set an element in a zero-value map will cause an error .

The judgment operation of map should be as follows:

if age, ok := ages["bob"];
	......

The second value okis a Boolean value, which is used to report whether the element exists. Maps cannot be compared directly, but must be compared through a custom function, so the absence of an element needs to be considered .

Although aggregated data such as slices, arrays, and structures cannot be directly compared and cannot be used as the key of a map, secondary mapping can be performed by converting strings .

4.10 Structure (struct)

A structure is an aggregate data type that combines zero or more named variables of any type, and each variable is called a member of the structure.

type Employee struct {
    
    
	ID 			int
	Name 		string
	money		int
    age,weight int
	......
}

Members of structures are accessed (including structure pointers) by dot notation, eg Employee.ID.

The definition order of the member variables of the structure is very important. Structure variables in different orders are different structure types .

If a structure member variable name is capitalized, then this variable can be exported . For Employee, only IDand Nameare exportable.

Within a structure, only structure pointers of the same type can be defined, thus creating a recursive data structure .

A structure without any member variables is called an empty structure, written struct{}without length and information, but can be used as a substitute for some type of bool value .

Structure type values ​​can be set through structure literals, that is, by setting member variables of the structure.

type Point struct{
    
     X, Y int}
p := Point{
    
    1, 2}

This initialization method must be initialized in the order of definition, and can also be initialized by specifying part of the member name .

p := Point(X:1, Y:2)

Even if you do not specify a name and initialize the structure in order, you cannot bypass the restriction of non-exportable variables, that is, lowercase variables .

Structures are usually used through pointers, as follows:

pp := &Point{
    
    1, 2}

Struct nesting and anonymous members:

type Point struct{
    
     X, Y int}
type Circle struct {
    
    
	Point
	Radius int
}
var c Circle

Go allows the definition of structure members without names, which is just a syntactic sugar. The essence is to replace them with types and allow them c.Xto be used . Whether anonymous members are allowed to be exported is determined by whether their type names are capitalized .

%#vObjects can be output in a Go-like syntax, which can include the names of member variables.

Guess you like

Origin blog.csdn.net/Once_day/article/details/129805642