Golang Learning Notes

[TOC]

Golang Learning Notes

This learning note was written as early as the beginning of 1.0, around the version, and may differ greatly from the latest version.

Because it was written earlier, and there were many own opinions in the article, some things that were not well understood at that time might have been written incorrectly. Some places have been supplemented and modified, there are omissions, Haihan.

This note was written according to two books <Learn go Language>and <go Language Practice>After reading it, I write notes while typing code, because I make a mistake when typing by hand.

Later, I also fused some of the things I learned in the video of Muchou. It is basically not difficult, mainly to understand the characteristics of go and some commonly used packages.

The code is here goSpider code Nor is the code a little different from the latest version, because it started on gitee and later decided to share it, it moved to github.

Common Golang Commands

  • go version view Golang version number
  • go help View go command help
  • go env looks at the Golang environment variable configuration and can see the configuration including GOPATH,GOROOT and other variable configurations.
  • go run Compile and run go program
  • go build compiler go program
  • go test run test file
  • go fmt formatting code
  • go clean cleans up compiled files
  • go get download package (walled)
  • go install Compile and install packages and dependencies
  • go list lists packages
  • go tool runs the tools provided by golang
  • go bug start error reporting mode

Install Golang

Environmental Change in GoPath

  • GOPATH is generally our own defined path to system environment variables

  • GOROOT is generally the path set by the go language itself, storing some packages in the go language, etc.

Linux and Unix default locations ~/go below

Default location for environment variables under window%USERPROFIL%\go

Official Recommendation: All projects and third-party libraries are placed under the same GOPATH

You can also place different items under different GOPATH s

go language compilation goes back to different GOPATH paths to find packages that you depend on

go language packages will be found in their original src directory, which we don't need to deal with

Our own import ed packages are found in the GOPATH we defined

Setting environment variables export GOPATH=/Users/liuhao/go

You can also set this in if you want to use it export PATH="$GOPATH/bin:$PATH"

Normally go sets environment variables by default, and when we write code and use import, the library of the system goes back to automatically find its own $GOROOT library. If we write libraries, we'll look in $GOPATH.

intellij IDEA automatically cleans up import errors in files

If you use the Goldand IDE directly, you don't need to do this, it will be removed automatically when you format your code

Old version idea has several options for On save in go inside Language & framework in system settings

The new version of idea needs to install the file watch in the plugin and add it to the file watch in the tools in the settings

My case of adding directories: /Users/liuhao/go/bin/go imports and then the useless imports will be cleared when the file is saved automatically or manually

  • Not to mention this
  • go fmt for formatting code only
  • go imports not only formats the code, but also cleans up and formats the wrong Import

Before that, we need to download and install this third-party library, otherwise you also do not have a goimports file, which will result in a Can't find goimports in GOPATH when automatically saved...

Use go get to get third-party libraries

Goget to get golang.org is not a good way to look Walled

To use gopm to get packages that cannot be downloaded

Install gopm

go get -v github.com/gpmgo/gopm

This is where we go back to our own GOPATH directory in ~/go

You can see that there's an additional directory for github.com under src under $GOPATH, and that's with our directory

    ~/go ⌚ 2:06:05
    $ tree -L 2
    .
    ├── bin
    │   └── gopm
    └── src
        ├── github.com
        └── learnGo

At this point we'll run the file at ~/go/bin/gopm

~/go/bin/gopm get -g -v -u golang.org/x/tools/cmd/goimports

After successful installation, there will be one more golang.org directory in src under $GOPATH along with our directory

~/go ⌚ 2:19:31
$ tree -L 2
.
├── bin
│   └── gopm
└── src
    ├── github.com
    ├── golang.org
    └── learnGo

Obviously, the directory where we encode is also running in src. bin is an executable

Executing the go install installation goimports installation in the ~/go/bin directory will generate an executable file in the ~/go/bin directory ~/go/bin ⌚ 2:26:38 $ go install ../src/golang.org/x/tools/cmd/goimports

After that, when we save the file, the typing will be automatically formatted according to the standard, and invalid import s will be deleted.

  1. go get command demonstration
  2. Use gopm to get packages that cannot be downloaded
  3. go build compiles the go file we write, but it will be built in the current directory
  4. go install generates Pkg files and executables to install go files we write under ~/go/bin Use go install. /... to install all go packages in the current directory When go install, there can only be one main function for a package, so go language main functions should be in their own directory
  5. go run direct compile run
~/go ⌚ 2:38:05
$ tree -L 2
.
├── bin
│   ├── goimports
│   └── gopm
├── pkg
│   └── darwin_amd64
└── src
    ├── github.com
    ├── golang.org
    └── learnGo

  • src has many third-party packages and our own projects in it. Each person stands in a directory
  • pkg and src correspond to some intermediate processes that we build out, so we don't have to care about them
  • bin is the executable we generated
src
    git repository 1
    git repository 1
pkg
    git repository 1
    git repository 1
bin
    //Executable 1,2,3,4...

Official online environment (walled)

http://play.golang.org/

Manual Installation

Domestic Forum Download Official Download

If you install manually, you need to set GOPATH and GOROOT environment variables manually before compiling

  • GOPATH is the project path to save
  • GOROOT Save Path for Packages to Download

Install under Mac

brew install golang
  • GOPATH will be in the go directory in your home directory
  • GOROOT will be at/usr/lib/go-version number

Installation under Ubuntu

sudo apt install golang
  • GOPATH will be in the go directory in your home directory
  • GOROOT will be at / usr/local/Cellar/go/1.11.2/libexec

Test for successful installation

go doc hash 

Golang Foundation

File Required Structure

package main //Must, stand-alone file must be called main

import "fmt" //Introduce a package for formatting IO

//Main function, stand-alone files must have a function with this name, which is called the same as C at the beginning of the code run
func main() {
//Print string contents
    fmt.Println("Hello")
}

variable

Be careful

go-defined variables will fail if not used

import imported packages will also fail if they are not used

64-bit integers and floating-point numbers are always 64-bit, whether they are on a 64-bit system or not.

Variable types are all independent, and mixing variable assignments can cause compiler errors, even with int32.

Once a string is assigned to a variable, it cannot be modified. Strings in go are immutable. They can only be made by converting to a rune array class

Definition and assignment of variables

When defining a go language variable, the type follows the variable name.

Variable declarations must be defined with var outside the function, and they can be assigned with var or: = inside the function (they cannot be used outside the function: =).

By: = you can define a variable without setting the type, or by indeterminate type. The system automatically infers the type by the value of the variable.

When a variable is defined with no value set, its default assignment is the default value for its type. This means int is 0 and string is an empty string.

Assignments of type int can be made in octal, hexadecimal, or scientific notation: 077,0xFF,1e3, or 6.022e23, which are all legal.

var age int
var name string
age = 20
name = "json"

Semicolons are not usually necessary, but they are necessary if you want to write more than one line.

var age int; var name string;
age = 20
name = "json"

In go language, variable declaration and assignment are two processes, but they can also be written together.

var age int = 20
var name string = "json"

Within a function, you can use the short tag: = to define a variable, but it is no longer available for subsequent assignments: = can only be used =

The type of variable defined in this way is inferred from the value of the variable.

age :=20 //int type
name = "json" //string type

Multiple variables can be declared in groups, as can const and import.

var (
  age int
  name string
)

Multiple variables of the same type can be declared in one line at the same time

var age, height int
age = 20
height = 180
//Parallel assignment can also be used
age, height := 20,180

Special variable name "" (underscore)

A special variable name is (underscore), and any value assigned to it is discarded.

For example, assign 180 to height 20 and discard 20

This form is useful when calling functions, such as a function that can return multiple functions.

When receiving with a variable, you can discard it this way by adding something you don't want

Because variables in go will fail if they are declared and not used

_, height := 20,180

For example, if declared here but not used, errors will occur when compiling

package main
func main(){
    var i int
}

Built-in function

go has a small number of functions that you can use without introducing any packages.

  • Close is used to close channel communication
  • new for memory allocation at various types of creation
  • panic throws an error when used for exception handling
  • Receive error when recover is used for exception handling
  • Delete is used to delete instances in the map
  • make is used for memory allocation at map, slice, channel creation
  • len returns string, slice, length of array
  • Cap is used to get the cap length of the array and slice
  • Append is used to append slice s
  • Copy is used to copy slice
  • Print underlying print function without line breaks
  • Pritln underlying print function, printing with line breaks
  • Complex handles complex numbers
  • real handles complex numbers
  • imag processing complex numbers

GoLang keyword (reserved word)

  • break
  • case
  • chan
  • const defines a constant
  • continue
  • default
  • func defines functions and methods
  • defer
  • go parallel operation (perform goroutine)
  • else
  • goto jumps to the label defined in the current function
  • fallthrough
  • if
  • for
  • import
  • interface Definition interface
  • map
  • package
  • range
  • return returns from function
  • Select to select different types of communication
  • struct for abstract data types
  • switch
  • Type type definition
  • var defines variables

Built-in data type

Sequence Number Type and Description
1 Boolean <br/>Boolean values can only be constant true or false.A simple example: var b bool = true.
2 Number types <br/>integer int and floating-point float32, float64, the Go language supports integer and floating-point numbers, and supports complex numbers, where the operation of the median uses complements.
3 String type: <br/>A string is a sequence of characters connected by a fixed length of characters.The Gos string is concatenated by a single byte.The bytes of the Go language string use UTF-8 encoding to identify Unicode text.
4 Derived types: <br/> include: <br/>(a) Pointer type <br/>(b) Array type <br/>(c) Structured type <br/>(d) Channel type <br/>(e) Function type <br/>(f) Slice type

<br/>(g) interface type <br/>(h) Map type

Boolean type bool

There's nothing to say about this, just like any other language.

The Boolean type identifies the Boolean decision value represented by the predefined constants true and false.

Boolean type is bool.

Number type

Sequence Number Type and Description
integer Integer type
1 Uint8<br/>unsigned 8-bit integer (0 to 255)
2 Uint16<br/>unsigned 16-bit integers (0 to 65535)
3 Uint32<br/>Unsigned 32-bit integer (0 to 429,67295)
4 Uint64<br/>unsigned 64-bit integer (0 to 18446744073709551615)
5 Int8<br/>Signed 8-bit integers (-128 to 127)
6 Int16<br/>Signed 16-bit integers (-32768 to 32767)
7 Int32<br/>Signed 32-bit integers (-2147483648 to 2147483647)
8 Int64<br/>Signed 64-bit integer (-9223372036854775808 to 9223372036854775807)
float float
1 Float32<br/>IEEE-754 32-bit floating point number
2 Float64<br/>IEEE-754 64-bit floating point number
3 Complex64<br/>32-bit real and imaginary numbers
4 Complex128<br/>64-bit real and imaginary numbers
Other Number Types Other Number Types
1 Byte<br/>similar uint8
2 Rune<br/>like int32
3 Uint<br/>32 or 64 bits
4 Int<br/>the same size as uint
5 Uintptr<br/>unsigned integer for holding a pointer

The int type represents an irregular length and will determine the appropriate length based on your hardware type

This means 32-bit on 32-bit hardware. 64-bit on 64-bit hardware.

Note that int is one of 32 or 64 bits and is not defined as another value.u int is the same (with u for unsigned)

If you want to specify the length, you can use int32 or uint32, and so on.

The complete list of integer types (signed and unsigned, unsigned u) is:

  • int8
  • int16
  • int32
  • int64
  • byte (alias for uint8)
  • uint8
  • uint16
  • uint32
  • uint64

Floating-point type values are:

  • float32
  • float64
  • There are no float types, only the two above

64-bit integers and floating-point numbers are always 64-bit, whether they are on a 64-bit system or not.

Note that these types are all independent

And mixing these types to assign values to variables can cause compiler errors

package main

func main() {
    var a int
    var b int32

    a = 15
    b = a + a
    b = b + 5
}
//results of enforcement
//./test1.go:8:4: cannot use a + a (type int) as type int32 in assignment

Assignments can be made in octal, hexadecimal, or scientific notation: 077,0xFF,1e3, or 6.022e23, which are all legal.

Constant constant

const age = 20 generates age as a constant. You can also use iota to generate enumeration types.

Constants are created at compile time and can only be numbers, strings, or Booleans.

Constant values can be used as a variety of types, so they can be automatically converted when float s are required for the following calculations

Constants can be of either specified or irregular type, and when irregular, they are of uncertain type.

Constant definition is not used and compiles without error

const age int = 20
const name string = "json"
const one, two = 1,2
const (
	cpp    = 0
	java    = 1
	python = 2
	golang = 3
)

Enumerate constant

Enumeration types in go do not have a specific definition paradigm

This can only be done by defining constants as defined by the iota keyword.

By default, the enumeration type declared by iota increases automatically from top to bottom.

The first iota represents zero, so a equals zero. When iota is used on the new line again, its value increases by 1, so b is 1

const (
  a  = iota //Default 0
  b = iota // Default 1
)

You can also omit the duplicate = iota by doing the following:

const (
  a = iota //Default 0
  b //Append an iota by default with a value of 1
)

If you want, you can also explicitly specify the type of the constant:

const (
  a = 0   //According to the go rule, undefined types are inferred from values, so the type is int
  b string = "0" //string type
)

Enumeration types can also set the step size

    const (
        a = iota * 10
        b
        c
        d
    )
    //The results are: 0,10,20,30

Enumeration types can also participate in operations as self-increasing seeds

    const (
        a = 1 << iota * 10
        b
        c
        d
        e
        f
    )
    //Results are 10,20,40,80,160,320

String string

The string in go is a sequence of utf-8 characters wrapped in double quotes (").

If wrapped in single quotation marks ('), a character is identified (utf-8 encoding), which is not a string in go

Once a variable is assigned a value, the string cannot be modified. Strings in go are immutable.

Developed from scratch C, the following situations are illegal in go:

var s string = "hello"
s[0] = 'c'  //Modify the first character to c, an error will be reported here

To modify characters in go, you need to do this

s :="hello"
c := []rune(s)  //Convert s to rune array
c[0] = 'c'  //Modify the first element of the array
s2 := string(c)  //Create a new string s2 to save the changes
fmt.Printf("%s\n", s2)  //Print results

A multi-line string, spliced by a plus sign (+), that parses escape characters, such as \n line breaks

This way, no matter how many lines there are, the output will be on the same line

s :="Strating part" + //The plus sign can only be here otherwise an error will occur
      "Ending part"

Multiline characters, written directly in inverted quotes (`), do not resolve escape characters, such as \n does not wrap

Wrap lines when multiple lines are present.

s := `Starting part
Ending part`

rune

Here rune is go's char

There is no char in go, only rune

Because char s have only one byte, they have many pits in many languages

Many characters in utf-8 are 3 bytes because GO uses a 4-byte int32 for rune

This byte is an 8-bit int, and rune is a 32-bit int. They can be mixed with integers

As the document says, they and the whole number are aliases

rune is an alias for int32, encoded with utf-8.

This type is typically used when traversing characters in a string

Each byte can be looped (only characters are equivalent when using US ASCII encoding, but not in go).

So to get the actual characters, you need to use the rune type

complex

go natively supports complex numbers, and its variable type is complex128(64-bit imaginary part).

If smaller, there are also imaginary parts of complex64 and 32 bits.

An example of using complex numbers:

var c complex64 = 5+5i;
fmt.Printf("value is : %v", c)
//Remember as: (5+5i)

error Error

go has built-in type for errors, error

The following defines err as an error type, and the value of err is nil

var err error

Array array

The definition of array is defined by [n] <type>, n stands for the length of the array, and <type> identifies the type of content stored.

Assigning a fire index to an element of array is done in square brackets:

var arr [10]int
arr[0] = 42
arr[1] = 13
println("this first element value %d\n", arr[0])

Array types like var arr=[10]int have a fixed size.

Size is part of the type, and since different sizes are different types, arrays cannot be resized.

Arrays are also of value type, and when one array is assigned to another, all elements are copied.

Especially when you pass an array into a function, it gets a copy of the array, not a pointer to the array.

You can declare a simple array var a[3]int like this without initializing the tower with a default value of zero.

Can also be abbreviated as a: =[...] int{1,2,3}

go automatically counts the number of elements, noting that all items must be set. Because if you use a multidimensional array, some items must be entered

a := [2][2]int{[2]int{1,2}, [2]int{3,4}}

Be similar to

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

Previous versions, when declaring an array, had to enter a write within square brackets, an array, or three points

However, new versions of array, slice and map make compound declarations simpler.

Using array,slice,map of a composite declaration, element composite declaration whose type is consistent with the outside can be omitted

The example above can be changed to

a := [2][2]int{{1,2},[3,4]}

slice

Slce and array are close, but they can be lengthened when new elements are added.

Slice always points to an array at the bottom. slice is a pointer to array, which is different from array

A slice is a reference type, which means that when one slice is assigned to another variable, the two references point to the same array.

For example, a function requires a slice parameter in which modifications to the slice element are also mentioned in the function caller, and a fold passes an underlying array pointer similar.

Create a slice with 10 elements by: sl: = make ([]int, 10).

The underlying array s are not different when you need to be aware of them.

A slice always appears in pairs with a fixed-length array that affects the length and capacity of the slice.

As follows, we first created an array of M element lengths, element type intvar array[m]int

Then create slice: slice for this array: =array[0:n]

And now there are:

len(slice) ==n
cap(slice) ==m
len(array) == cap(array)==m

A comparison of array and slice

Given an array or other slice, a new slice is created by a[I:J].

This creates a new slice pointing to variable a, which is intercepted from where I is needed to where J is needed. Eliminate poison as J-I.

//array[n:m] creates a slice from array with elements n to m-1
a :=[...]int{1, 2, 3, 4, 5} //Define an array with five elements
s1 := a[2:4]  //Intercept the subscript 2-3 element and create a slice containing elements 3,4
s2 := a[1:5]  //Intercept the subscript 1-4 element to create a slice containing elements 2,3,4,5
s3 := a[:]  //Create a slice with all the elements in array, which is a simplified notation for a[0:len(a)]
s4 := a[:4] //Intercept 0-3 to create slice, which is a simplified notation of a[0:4], resulting in 1,2,3,4
s5 := s2[:] //Intercept all elements from slice s2, create a new slice, note that s5 still points to array a

The code below shows an array slice that exceeds the array range when assigning a specified subscript

package main
func main(){
  //Declares an array of length 100 with an int value
  var array [100]int
  //Intercept the array
  slice := array[0:99]

  slice[98] = 'a' //Successful assignment
  slice[99] = 'b' //You'll notice here that index out of range
}

If you want to extend slice, you can use append and copy.

  • Append wants slice to append a new value and return the appended new slice of the same type as the original.
  • If the slice does not have enough capacity to store the added value, append allocates a new slice large enough to hold the elements of the original slice and the added value.
  • The slice returned may therefore point to a different underlying array.
s0 := []int{0,0}
//Notice the following three ways to append
s1 := append(s0, 2) //Append an element, s1 == []int{0,0,2}
s2 := append(s1, 3, 4, 7) //Append multiple elements, s2 == []int{0,0,2,3,5,7}
s3 := append(s2, s0...)  //Append a slice, s3 ==[]int{0,0,2,3,5,7,0,0}

The function copy copies elements from the source slice src to the target dst, and returns the number of elements copied.

The source and target may be duplicated, and the number of copies is the minimum in len(src) and len(dst)

//It's still an array here, slice when you cut below
var a =[...]int{0,1,2,3,4,5,6,7}
//Create a slice of 6 length
var s = make([]int,6)
n1 := copy(s,a[0:]) //n1 ==6, s== []int{0,1,2,3,4,5}
n2 := copy(s,s[2:]) //n2 ==4, s== []int{2,3,4,5,4,5}

map

Many languages have similar types.go has map types.

map can be thought of as an array indexed by a string (in its simplest form)

The general way to define a map is map[<from type>]<to type>

The map type is defined below to convert a string (abbreviated for English month) to an int-the number of days in that month.

monthdays := map[string]int{
  "Jan":31,"Feb":28,"Mar":31,
  "Apr":30,"May":31,"Jun":30,
  "Jul":31,"Aug":31,"Sep":30,
  "Oct":31,"Nov":30,"Dec":31, //There must be a comma at the end
}

Note that when you only need to declare a map, use the form make: monthdays: = make (map[steing]int)

When indexing (searching) in a map, use square brackets for values, such as how many days of December are printed: fmt.Printf("%d\n", monthdays["Dec"])

When iterating through an array,slice,string, or map loop, you can use range, which returns a box of keys and corresponding values each time you call it

year :=0
for _,days :=range monthdays{ //The keys here are useless, so use _no assignment
  year +=days
}
fmt.Printf("Numbers of days in a year :%d\n",year)

Add elements to the map, you can do so

monthdays["Undecim"] = 30 //Add a month
monthdays["Feb"] = 29  //Rewrite this element in leap years

Check to see if this element exists, you can do this

var value int
var persent bool

value,present = monthdays["Jan"]  //present value is true if the subscript exists, false otherwise
v,ok := monthdays["Jan"]  //Write this way closer to go, if ok is true otherwise false

You can also remove elements from a map by subscribing

Typically, the statement delete(m,x) deletes an instance of a map created by m[x]

delete(monthdays,"Mar") //Remove elements marked Mar

operator

Arithmetic Operators

The following table lists the arithmetic operators for all Go languages.Assume A value of 10 and B value of 20.

operator describe Example
+ Addition A + B
- subtract A - B
* Multiplication A * B
/ be divided by B / A
% Remainder B % A
++ Self-increasing A++
-- Self-decreasing A--

Relational Operators

The following table lists the relational operators for all Go languages.Assume A value of 10 and B value of 20.

operator describe Example
== Check that the two values are equal, and return True if they are equal or False if they are not. (A == B) is False
!= Check that the two values are not equal and return True if they are not equal or False if they are not. (A!= B) is True
> Check if the left value is greater than the right value, if True, otherwise False. (A > B) is False
< Check if the left value is less than the right value, if True, otherwise False. (A < B) is True
>= Check if the left value is greater than or equal to the right value, if True, otherwise False. (A >= B) is False
<= Check if the left value is less than or equal to the right value, if True, otherwise False. (A <= B) is True

Logical operators

The following table lists the logical operators for all Go languages.Assume that A is True and B is False.

operator describe Example
&& Logical AND operator.If both operands are True, the condition is True, otherwise it is False. (A && B) is False
|| Logical OR operator.If the operands on both sides have a True, the condition is True, otherwise it is False. (A || B) is True
! Logical NOT operator.If the condition is True, the logical NOT condition is False, otherwise it is True.

Bitwise Operators

Bit operators operate on the binary bits of integers in memory.

The following table lists how bitwise operators &, |, and ^ are calculated:

  • First line: P & Q = 0, P | q = 0, p ^ q = 0 when p=0,q=0;
  • Line 2: P & Q = 0, P | q = 1, p ^ q = 1 when p=0,q=1;
  • ....... and so on
p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

The bit operators supported by the Go language are shown in the following table.Assume A is 60 and B is 13:

operator describe Example
& The bitwise and operator'&'is a binary operator.Its function is that the corresponding binary phases of the two numbers involved in the operation are and, at the same time, are "1", and the result is "1", otherwise it is 0. (A & B) result is 12, binary is 0000 1100
| The bitwise OR operator'|'is a binary operator.Its function is the binary phase or the corresponding binary phase of the two numbers involved in the operation.If only one of the two objects participating in the operation is 1, its value is 1. (A | B) result 61, binary 0011 1101
^ The bitwise exclusive or operator'^'is a binary operator.Its function is that the corresponding binaries of the two numbers involved in the operation are different or, when the corresponding binaries are different, the result is 1.<br/> can be used as either a binary or a unary operator, and a unary operator representation is a bit-wise negation.The binary operator <br/> is XOR, the same 0, the different 1. (A ^ B) result 49, binary 0011 0001
<< The left shift operator'<<'is a binary operator.A left shift of N bits is the n-th power multiplied by two.The <br/>function shifts all binaries of the operand to the left of'<<'by several bits, specifies the number of bits to move by the number to the right of'<<', discards the high bits, and supplements the low bits by 0. A << 2 Result 240, Binary 111 0000
>> The right shift operator'>'is a binary operator.A right shift of N bits is the n-th power divided by 2.The function of <br/> is to move all binaries of the operand to the left of'>'a few bits to the right, and the number to the right of'>' specifies the number of bits to move. A >> 2 results in 15, binary 0000 1111

Assignment Operators

The following table lists the assignment operators for all Go languages.

operator describe Example
= A simple assignment operator that assigns the value of an expression to a left value C = A + B assigns the result of an A + B expression to C
+= Add before assigning C += A equals C = C + A
-= Subtract and assign C -= A equals C = C - A
*= Multiplication before assignment C *= A equals C = C * A
/= Divide before assigning C /= A equals C = C / A
%= Re-assign after remainder C%= A equals C = C% A
<<= Assignment After Left Move C <<= 2 equals C = C << 2
>>= Assignment after moving right C >>= 2 equals C = C >> 2
&= Bitwise and post-assignment C &= 2 equals C = C & 2
^= Bitwise XOR Post-assignment C ^= 2 equals C = C ^ 2
= Bitwise or post-assignment C

Other Operators

The following table lists the other operators for the Go language.

operator describe Example
& Return variable storage address &a; The actual address of the variable is given.
* Pointer variable. *a; is a pointer variable

Operator Priority

Some operators have a higher priority, and binary operators operate from left to right.The following table lists all the operators and their priorities, with top-down representing the priority from high to low:

priority operator
7 ^ !
6 * / % << >> & &^
5 + - | ^
4 == != < <= >= >
3 <-
2 &&
1 ||

structure control

go language has few structural control statements, no do while or while loops

Only:

  • if condition judgment
  • for loop
  • A switch can also have an initial value like for
  • select type selection and multiplexer

if statement

Basically this is how it is written. go language forces braces and requires the same line.

n := 0
if n > 0 {
  return 1
} else if n < 0{
  return 2
} else {
  return 3
}

Both if and switch support initialization statements, which are often used to set a local variable

Receive the processing results and simultaneously judge the results

  //For example, first receive the os.Create creation result and determine if the error returned by the result is nil
	if handle, err := os.Create("./nihao"); err == nil {
    print("File created successfully\n")
    //Also receive handle.Write file write results, and determine if the error returned is nil
		if _, err := handle.Write([]byte("11111")); err == nil {
			print("File content written successfully\n")
		} else {
			print("File content write failed\n")
		}
		handle.Close()
	} else {
		print("File creation failed\n")
		handle.Close()
	}

The above initialization statements are of the same nature as using logical operators

  //Create a file and receive the result of creating the file
  handle, err := os.Create("./nihao")
  //Judging Creation Results
	if err == nil {
    print("File created successfully\n")
    //Write content, receive write content results
    _, err := handle.Write([]byte("11111"))
    //Determine Write Results
		if err == nil {
			print("File content written successfully\n")
		} else {
			print("File content write failed\n")
		}
	} else {
		print("File creation failed\n")
	}
	handle.Close()

Logical operators can also be used as usual

if true && true {
  print("true")
}

if !false {
  print("false")
}

goto Statement

Use goto statements with caution

Use goto to jump to the label defined within the current function (must be the current function), label name case sensitive

//Dead-loop function
func myFunc() {
	i := 0
Here: //Label name is arbitrary
	print(i)
	i++
	goto Here //Jump to the label to continue execution
}

for statement

The go language has three forms of for loops, only one of which uses semicolons.

  • As with the for loop of C, use semicolons in the middle
for init; coditoin; post{}
  • Like C's while
for condition{}
  • Like C's for(), it is a dead loop
for{}  

Simple for loop, short declaration

sum :=0
for i :=0; i < 10; i++ {
    sum +=i
}

go has no comma expression, while ++ and - are statements rather than expressions

If you want to execute multiple variables in for, you should use parallel assignment

//for-loop band parallel assignment
for i, j := 0, len(a) - 1; i < j; i, j = i + 1, j - 1 {
  //This is also a parallel assignment
  a[i], a[j] = a[j],a[i]
}

brek exits the loop and continue skips the loop

for i:=0; i<10; i++{
  if i>5{
    break//End the cycle
  }
  print(i)
}

When nesting loops, you can specify a label while looping. break specifies a label to determine which loop is terminated

No spaces at labels and for loops

//There can be no spaces in the label and for loop here
J:for j:=0; j<5; j++{
    for i :=0; i<10; i++{
      if i>5 {
        break J;//Exit the loop J at the J tag, not the loop i
      }
      print(i);
    }
  }

Use continue to move the loop to the next iteration, with slightly excess code.

for i:=0; i<10;i++{
  if i>5{
    continue
  }
  print(i)
}

range keyword

The keyword range can be used to loop slice,array,string,map,channel.

range is an iterator that, when called, returns a key-value pair from the contents of his loop, and ranges return different things depending on what they are.

For slice or array loops, range returns a sequence number as the key, which requires the corresponding content as the value.

list := []string{"a","b","c","d","e","f"}
for k, v := range list{
  println(k)
  println(v)
}

Use range directly on string strings so that strings are also counted as separate Unicode strings

And the starting position is interpreted as UTF-8

  //Traverse through these three strings
	for pos, char := range "aΦx" {
		fmt.Printf("character '%c' starts at byte position %d\n", char, pos)
  }
  //results of enforcement
//GOROOT=/usr/local/go #gosetup
//character 'a' starts at byte position 0
//character 'Φ' starts at byte position 1
//character 'x' starts at byte position 3

switch Statements

go language switch case will break automatically by default after hitting

switch doesn't need break, but fallthrough if you don't want break

switch can set the default value by default once all conditions fail to match

Case may not have a case body

The expression does not have to be a constant or an integer, executing from top to bottom until a match is found.

A switch can be followed by no expression, as long as it is defined in the case. If no expression exists, it matches true, which is equivalent to if true going into a loop automatically.

Here if-else if-else judgment sequence is written by switch ing

//Note that when passing a parameter here, keep the type consistent, otherwise it cannot be computed
func unhex(c byte) byte {
	switch {
	case '0' <= c && c <= '9':
		return c - '0'
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10
	case 'A' <= c && c <= 'F':
    return c - 'A' + 10
  default:
    println("no case")
	}

	return 0
}

When a switch condition is hit, it does not continue running down but exits automatically. The following case 1 will not execute

i :=0
switch i{
  case 0: //This is an empty case body, but 0 matches i
  case 1:
  //Later will not be executed
    println("case 1")
  default:
    println("default")
}

To continue down after a match fails, you need to use fallthrough

i :=0
switch i{
  case 0: fallthrough //This is an empty case body, but 0 matches i
  case 1:
  //This will be executed
    println("case 1");
  default:
    println("default")
}

A case can be multiple items, separated by commas. Determine if a parameter is in multiple items listed

shouldEscape('=')
//Determines whether the incoming symbol is in the specified list
func shouldEscape(c byte) bool{
  switch c{
    //This is similar to if c=='' || c=='?'.......
    case ' ', '?','&','=','#','+':
     return true
  }
  return false
}

Comparing byte arrays using switch

  //Declare parameters, call functions
	var a  = []byte{1,2,3,4,5}
  var b  = []byte{2,3,4,5,6}
  //Or using uint8, mainly to see that byte is actually uint8
  //var a  = []uint8{1}
	//var b  = []uint8{2}
  compare(a, b)
  
//Compare returns an integer in the order of two character array Dictionaries
//If a ==b returns 0, if a,b returns -1, if a>b returns + 1
func compare(a, b []byte) int{
  for i :=0; i<len(a) && i<len(b); i++{
    switch{
      case a[i] > b[i]:
      return 1
      case a[i] < b[i]:
      return -1
    }
  }

  //Different lengths do not equal
  switch {
    case len(a) < len(b):
      return -1
    case len(a) > len(b):
      return 1
  }
  return 0  //String equality
}

function

Functions are the fundamental building blocks for Go s; everything interesting happens in them.

Function Definition

Customize a new type (like objects in other languages, but go has a richer usage)

type myType int

Define a custom function

func (p mytype) funcname(q int) (r, s int) { return 0,0 }
  • func keyword, used to define a custom function
  • (p mytype) Function definitions can be based on a specific type (custom or predefined), which becomes a method. This part is a receiver and he is optional
  • funcname is the name of the custom function
  • Parameters and parameter types received by q int custom function
  • (r, s int) Specifies the return value and return type of the custom function. go can return multiple values. If you don't want to name the returned parameters, just provide the return type, such as: (int,int). If there is only one return value, parentheses can be omitted. If the function is a sub-procedure and there are no return values, you can not define this thing
  • {return 0,0} This is the body of the function, note that return is a statement

Here are two examples: one function does not return a value, the other simply returns the input

//no return value
func subroutine(in int){
  return
}

//Return input
func identity(in int)int{
  reutn in
}

Functions can be defined in any order, and the compiler scans each file before execution.

So the function prototype is a state-owned old thing in go. go does not allow function nesting, but we can use anonymous functions to implement it

Recursive functions work like any other language

func rec(i int){
  if i== 10{
    return
  }
  re(i+1)
  //Function here, the printed result is negative, and the printed line is positive before recursive call
  fmt.Printf("%d ", i)
}
//Print results: 9 8 7 6 5 4 3 2 1

Function Scope

In the go language, variables defined outside of a function are global, and in fact are only global within the package.

Variables defined within a function are local to the function.

A local variable and a global variable. When a function is run, the local variable overrides the global variable.

package main

var a =6  //global variable

func main(){
  p()//The output is a global variable with a value of 6
  q()//Call a function to override the value of a global variable
  p()//The output is a value of 5 for the local variable
}
func p(){
  println(a)  //Print variables only
}

func q(){
  a :=5 //Local variable, variable a is reassigned when the function is called
  println(a)
}

//Execution result is 656

When a function calls a function, the scope of the variable is limited to the function defined by the variable.

Notice the difference between this call below and the one above, that is, calling functions one by one, and calling functions within functions

package main

var a int

func main(){
  a = 5
  println(a)  //Print 5, because the initial content is 5, this is also the first time you print
  f()
}

func f(){
  a := 6
  println(a)
  g()
}

func g(){
  println(a)
}
//The output will be 565, because local variables are only valid when the defined function is executed

Function returns multiple values

go's functions and methods can return a number of values

For example, the Write function returns a count value and an error because not all of the bytes we passed in were successfully written to the file, possibly due to a device exception.

This is how *File.Write is declared in OS packages

func (file *File) Write(b []byte) (n int, err error)

As the document says, he returns the number of bytes written and non-nil error s, which are common in Go s.

Similar methods avoid passing pointer analogue reference parameters to return values.

For example, take a value from a specified bit of a byte array and return the value and the next location.

func nextInt(b []byte, i int)(int, int){
   n := 0
   for ; i < len(b); i++{
     n = n*10 + int(b[i]) - '0'
   }

   return n, i
}

//We can scan the input array for numbers and call the function at the same time
a :=[]byte{'1','2','3','4'}
var n int
for i := 0; i < len(a); {
  n,i = nextInt(a,i)
  println(n)
}

Without tuples as native types, multiple return values may be the best choice, and we can accurately return the desired values without overloading the domain space to a specific error signal.

Named Return Value

The function return value or result parameter of the go language can be given a name and used like the original variable, just like the input parameter.

If named, at the beginning of the function, they will be initialized with their type defaults.

If the function returns without specifying a variable, it simply returns. The last assignment of a variable that has the same name as the return parameter will be returned.

Function definition, function name first, return type can pass in an anonymous function, variable parameter list, can return multiple parameters, no default parameters, optional parameters, function overload, operator overload,

Parameters are separated by commas, with the same name preceding them, and types become red here because functions with that name are also declared in other files

func nextInt(b []byte,pos int)(value, nextPos int){/* ... */}

Since the named results are initialized and associated with unmodified return s, they can be very simple and clear (which I find difficult to read).

I think that if you return multiple values, don't use them indiscriminately. Generally, you return a single value and an error message

//When a function is defined, both the return value parameter n and err have type default values. n is 0,err is nil
func ReadFull(r Reader, buf []byte) (n int, err error){
  for len(buf) >0 && err ==nil {
    var nr int
    nr, err = r.Read(buf)
    n += nr
    buf = buf[nr:len(buf)]
  }
  //Modifying return here directly returns the last assignment of a variable named n and err
  return
}

In cases where multiple values are returned, define a name for the return value, but if you name it, think about it. Otherwise, it will be hard to read if it is scattered in the function body and you don't know where to call it.

func div(a, b int) (q, r int) {
	//When two values are returned, if you only want one, the other value can be represented by _which means you don't accept the value
	//Inside does not move, but outside returns also need to be picked up by name, where q,r is just the name in the function body, outside can be arbitrarily picked up, called a,b also does not matter, but it is best to be consistent
	//You can also write this here
	//return a / b, a % b
	//But it's best to do this because the name of the returned value is defined, but once the body of the function is longer, it's hard to read, and for longer, it's better to use the same as above
	q = a / b
	r = a % b
	return
}

defer code

Code that follows the same line of defer keyword will not execute until the end of the function call

Follow FIFO principle when multiple defer s

Suppose you have a function that opens a file and reads and writes it. In such a function, there are often places where errors might occur that need to be returned early. As a general rule, you need to close the file descriptor once for every run-time contact.

This often results in the following code:

func ReadWrite() bool{
  file.Open("file")
  //Do something
  if failureX {
    file.Close()//Close File Descriptor
    return false
  }

  if failureY{
    file.Close()//Close File Descriptor
    return false
  }
  file.Close()//Close File Descriptor
  return true
}

The code above has a lot of duplicate code because it close s.

go has a defer statement, and the function you make after defer is called before the function exits.

The above code can be changed to the following so that Close is placed after Open

func ReadWrite() bool{
  file.Open("file")
  defer file.Close()//Close File Descriptor
  //Do something
  if failureX{
    return false  //If it ends here, Close will be called automatically
  }
  if failureY{
    return false  //If it ends here, Close will be called automatically
  }
  return true //Close runs automatically when execution is complete
}

Multiple functions can be put into a deferred list and executed in a FIFO order

for i :=0; i<5; i++{
  //This is equivalent to defining five defaer s, defer println(1),defer println(2).....
  defer println(i)
}
//The output is: 43 2 1 0

Using defer even modifies the return value

By defining defer before being called, let the function execute last. Let's return again.

//This defer here causes the function to delay execution
defer func(n int){
  //todo
}(5)  //Parentheses are required here


func f() (ret int){ //ret initialization is 0
  defer func(){
      ret++ //Increase ret to 1
  }()//Parentheses are required here

  return 0    //Return value is 1 instead of 0
}

Variable parameters

A function that receives a variable parameter has an indefinite number of parameters, that is, any number of parameters can be passed in

A variable with a variable parameter, within a function is a slice of the same type as the parameter specification.

//arg...int tells go that this function accepts any number of arguments, but must be int-type
func myfunc(arg ...int){
  //In a function, the variable arg is a slice of type int
  for _, n :=range arg{
    println(n)
  }
}

If the type of variable parameter is not specified, the default is empty interface {}

Assuming a function with another variable parameter is called myfunc2, the following example shows how to pass parameters to other functions

func myfunc(arg ...int){
  myfunc2(arg...) //Pass as is, without changing incoming parameters
  myfunc2(arg[:2]...) //Cut the incoming parameter before passing it
}

Function as value

Like everything else in GO, functions are just values.

Functions can be assigned to variables like this

func main(){
  a := func(){    //Define a function without a function name (an anonymous function) and assign it to variable a
    println("nihao")
  } //There is no ()

  a()//Call function
}

If you use fmt.Printf ('%T\n', a) to print the type of variable a, you can see that the output will be func()

Functions as values are also used in other places, such as map s,

var funcMap = map[int]func() int{
  1: func() int{return 10},
  2: func() int{return 20},
  3: func() int{return 30},
}

Anonymous functions and closures

When an anonymous function is defined, it executes without a call, but it can be passed by

package main

func main(){
  fun := func (p1 int,p2 int) int{
    return p1 + p2
  }(10,20)  //

  println(fun)
}

Assigning an anonymous function to a variable invokes the anonymous function through the variable

package main

func main(){
	fun := func(n1 int, n2 int) int{
		return n1 + n2
	}
	println(fun(10,20))
}

A global anonymous function is one that assigns an anonymous function to a global variable, and this anonymous function is called in the current program.

package main

var (
  fun = func(n1 int,n2 int) int{
    return n1+n2
  }
)

func main(){
  println(fun(10,20))
}

Closure is a function that encapsulates the surrounding state at creation time.Even if the environment in which the closure resides no longer exists, the state of the encapsulation in the closure still exists.

The anonymous function of the GO language is the closure, which is explained in GO Language Programming

 Basic concepts
 A closure is a block of code that can contain free (unbound to a particular object) variables that are not within the block or
 Defined in any global context, but in the environment where the code block is defined.Code block to execute (because free variables contain
 In the code block, so these free variables and the objects they refer to are not released) provide bound calculation rings for free variables
 Scope.
 Value of closures
 The value of a closure is that it can be used as a function object or an anonymous function, which for a type system means more than just representing
 The data also represents code.Most languages that support closures use functions as first-level objects, meaning they can be stored in
 Variables are passed as parameters to other functions and, most importantly, can be dynamically created and returned by functions.

Closures are commonly used with anonymous functions and can be used to access local variables in a function (variables being accessed are pointer-to-pointer relationships and operate on the same local variable), such as:

package main

func closure(x int) (func(), func(int)) {
    fmt.Printf("initial value x For:%d,Memory address:%p\n", x, &x)
    f1 := func() {
        x = x + 5
        fmt.Printf("x+5: %d,Memory address:%p\n", x, &x)
    }
    f2 := func(y int) {
        x = x + y
        fmt.Printf("x+%d: %d,Memory address:%p\n", y, x, &x)
    }
    return f1, f2
}
func main() {
    func1, func2 := closure(10)
    func1()
    func2(10)
    func2(20)
}

callback

When a function is a value, it can be easily passed to other functions and then used as a callback function.

func prointit(x int){ //Function has no return value
  fmt.Print("%v\n", x)  //Print only
}

The identity of this function is func printit(int), or a function without a function name: func(int)

Creating a new function using this as a callback requires this identity

//This function means that the variable y must be int and the variable f must be a function
func callback(y int, f func(int)){  //The variable f will hold the incoming function
  f(y)  //Callback function f, passing in variable y
}

Complete callback function case

//The go language is a functional programming language in which a set of functions can be used as a composite function (callback function)
//The main flow, we put the parameters a and B we received into op() to execute and return an Int
//Function in apply, we put the two parameters a and B that we received into the first function op(), and op() returns an int
//Detailed ope() takes two parameters and returns an int
//applay receives two parameters a and B returns an int
func apply(op func(int, int) int, a, b int) int {
	//Output function call name
	fmt.Printf("Calling %s with %d, %d \n", runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), a, b)
	//Put parameters received by apply into op() to execute
	return op(a, b)
}

	//Call a function by passing in an anonymous function
	fmt.Println(apply(func(a int, b int) int {
		return int(math.Pow(float64(a), float64(b)))
	}, 3, 4))

Panic and Recover

go has no exception handling mechanism like other languages and cannot throw exceptions.

Instead, it uses panic-and-recover mechanisms.

This should be used as a last resort, something we should not, or rarely use, in our code.

Panic

Is a built-in function that immediately interrupts the execution of an existing program.

For example, when function F calls panic(), the execution of function F is interrupted, but the defer in F executes normally, and then F returns to where it was called.

Where he is called, F behaves like a panic() call. This process continues to go up and execute each defer until all goroutine s return when the program crashes

Panic can be generated directly through the panic() function or by running errors, such as accessing arrays that are out of bounds

Recover

Is a built-in function that allows goroutine s that enter the panic process to recover, which is only valid in defer

During normal execution, calling recover returns nil with no other effect.

If the current goroutine is in panic, call recover to capture the input value of the panic and resume normal operation

The following is a case of panic detection using recover, which checks whether a function as a parameter will generate a panic when it is executed:

//Define a new function thrwsPanic accepts a function as a parameter, returns true when the function produces panic, false otherwise
func throwsPanic(f func()) (b bool){
  //Defines a function that uses recover's defer. If the current goroutine generates a panic, the defer function can discover that when recover() returns a non-nil value, set b to true
  defer func(){
    if x := recover(); x != nil{
      b = true
    }
  }()
  f() //Call a function received as a parameter
  return  //There is no need to return the value of B because B is a named return value
}

Complete error handling cases:

package main

import (
	"errors"
	"fmt"
)
/**
panic
 Stop the current function
 Go back up and execute each defer
 If you do not encounter a recover program exit

recover
 Called only in defer
 panic values can be obtained in defer by recover()
If not, panic again
 */

func tryRecover() {

	//Anonymous functions must be followed by a ()
	defer func() {
		r := recover()
		//If it's an error message, the output error message is just a line of error information. Without this error handling, the whole output would be ugly
		//r.(error) is a type assertion here, meaning that I infer r is an error type
		if err, ok := r.(error); ok {
			fmt.Println("Error occurred", err)
		} else {
			//If you don't know what it is, don't know what to do, output directly
			panic(fmt.Sprintf("I don't know what to do%s", r))
		}

	}()

  //Error information that can be handled properly, but can not be handled properly below because of the error type judgment made in the anonymous function above
  //But there's nothing wrong with panic() panic reception
	//panic(errors.New("this is an error"))

	//For example, now this will output Error occurred runtime error: integer divide by zero
	//Otherwise, it's a bunch of code error messages
	//b := 0
	//a := 5 / b
	//fmt.Println(a)

	//This direct output error message, however, is not captured by recover above
	//So go straight to panic(fmt.Sprintf("I don't know what to do%s", r))
	panic(123)

}

func main() {
	tryRecover()
}

package

Define Package

A package is a collection of data and functions defined using the package keyword.

The file name does not need to match the package name, which conventionally uses lowercase characters.

A go language package can consist of multiple files, but there can only be one main file mainpackage main function

package even

//Capitalize the first letter to indicate a public method that can be called outside the package
func Even(i int) bool{
  return i% 2==0
}
//Lowercase first letter, indicating a private function
func odd(i int) bool{
  return i%2 ==1
}

Resume a directory under $GOPATH, copy even.go to this directory

mkdir $GOPATH/src/even
cp even.go $GOPATH/src/even
go build
go install 

Use even in other programs

In go, when the first letter of a function is capitalized, the function is exported from the package, so it is OK to call Even and error to call odd

package main
import(
  "even"//Import the package we just wrote
  "fmt"//Import official packages
)
func main(){
  i := 5
  //Call the public function in the even t package we just wrote
  fmt.Printf("is %d even? %v\n", i, enen.Even(i))
  //There was an error calling the private function we just wrote.
  fmt.Printf("is %d even? %v\n", i, enen.odd(i))
}

To summarize

  • The name of a public function begins with an uppercase letter
  • Private functions begin with a lowercase letter

This rule also applies to other names in the package (new types, global variables)

Be careful:

Capital letters can mean anything from Greek to Ancient Egyptian

Package Name

When a package is imported (via import), the package name becomes the entry to the content

After importing "bytes", you can import the package file and call the function bytes.Buffer.

Anyone who uses this package can access its contents under the same name, so it's good, short, and easy to remember.

According to the specification, a package name is a lowercase word and should not be underlined or mixed case.

A package can be named by import bar "bytes" and he becomes a bar.Buffer

Another specification is that the package name is the root directory name of the code, and the package in src/pkg/compress/gzip is imported as compress/gzip, but the name is gzip, neither compress_gzip nor compressGzip

Import packages will reference content by their name, so imported packages can use this to avoid verbosity.

For example, the Buffer Bufio package read method is called Reader, not BufReader, because the user sees the clear, concise name bufio.Reader.

Further, bufio.Reader will not conflict with io.Reader since the imported instances are always the addresses to which their package names point.

Similarly, ring.Ring creates a new instance function and defines the constructor in go, often called NewRing

But since Ring is the only exported type of this package, and this package is also called ring, he can just call it New.

The user of the package sees ring.New and uses the package structure to help you choose a better name.

Import Package

import(
  "fmt"
  "strings"
)

The compiler first looks in the go installation directory, then in order for the directories listed by the GOPATH variable to find packages that satisfy the import statement

By introducing relative paths to find packages on disk, packages in standard libraries are found in the go installation directory, and packages created by GO development are found in the GOPATH environment variable specified directory

These directories specified by GOAPTH are the developer's personal workspaces

For example, if GO is installed at/usr/local/go and the environment variable GOAPTH is set to/home/myproject:/home/mylibraries/, the compiler will look for net/http packages in the following order

/usr/local/go/src/pkg/net/http  # This is where the source code for the standard library is located
/home/myproject/src/net/http
/home/mylibraries/src/net/http

Remote Import

import "github.com/spf13/viper"

When using the import path compiler, the go build command uses the GOAPTH setting to search for this package on disk.

In fact, this import path represents a URL to the code base on GitHub.

If the path contains a URL, you can use the go tool chain to get the package from DVCS and save the source code of the package in a directory where the path specified by GOPATH matches the URL.

This acquisition takes too long to complete using the go get command, which will get the package of any specified URL, or another package on which an imported package lock depends

Because of this recursive nature of go get, this command scans the source tree of a package to get all dependent packages it can find

Name the import (alias the package)

If you want to import multiple packages with the same name, such as network/convert and file/convert.

Two packages named conver will be imported at the same time. In this case, a package with a duplicate name can be imported by command import.

Named import refers to defining a name to the left of the package path given in the import statement, and naming the imported package a new name

package main

import(
  "fmt"
  myfmt "mylib/fmt"
)

func main(){
  fmt.Println("standard library")
  myfmt.Println("mylib/fmt")
}

When we import a package that is no longer used in the code, the go compiler fails to compile and outputs an error.

Sometimes you need to import a package, but you don't need it. You can rename the package with a blank identifier (underscore).

init function

There's nothing to say about this, just like u construct in PHP

Each package can have any number of init functions that are called before the program executes

package main
var name string = "nihao"

func main(){
  println(name)//This outputs haha instead of nihao
}
//First called
func init(){
  name = "haha"
}

identifier

Just like in other languages, the name go is important.

There may even be semantic left and right, for example, whether or not the out-of-package is visible depends on the size of the first letter.

Rules are used to keep well-known abbreviations as they are, not to try where they should be capitalized.

Aoio,Getwd,Chmod

Humps are great for those with complete words: ReadFileNewWriteMakeSLice

Package Documentation

Each package should have a package comment, a comment block before the package.

For multi-file packages, the package comment only needs to appear before one file, any file can.

Package comments should introduce the file and provide overall information about the package.

This time it appears on the package page generated by go doc, and the details are shown together

Examples from the official regexp package:

/*
The regexp package implements a simple library for regular expressions.
The syntax of the regular expressions accepted is:
regexp:
concatenation '|' concatenation
*/
package regexp

Each defined and exported function should have a short paragraph describing the behavior of the function, from an example of the fmt package

// Printf formats according to a format specifier and writes to standard // output. It returns the number of bytes written and any write error
// encountered.
func Printf(format string, a ...interface) (n int, err error)

Comments written above the function to generate the document

Command Line View Document

Documents in comments to the current function that can be viewed using go doc in the current directory

For example, go doc If you want to view the entity's function go doc function name, such as: go doc push and go doc Queue

Use go help doc to also see function descriptions inside go's packages, such as go doc fmt.Println View the document by starting a web service

Godoc-http:6060 will start a service, and then you can view a page through localhost:6060, which is similar to the official website with documentation and package instructions

The page can also see the documentation for the functions we wrote in the project, but our functions need comments to have documentation, otherwise only functions

A comment above a function, followed by a comment on the same line, is displayed as a document, and the comment is written arbitrarily without specifying a format.

If you start a comment on the next line of the comment with four spaces at the beginning, it is considered a code case presentation

Sample Code

package queue

import "fmt"

//Sample code for Pop within Queue
func ExampleQueue_Pop() {

/**
//The following Output: Content is the expected return value when used as a test and is required, and the comments on both lines are also shown in the sample code
//The sample code is not only the sample code, but also checks to see if the value of Output is correct. Here is an intentional error
//Notes here can't be scribbled. Notes written here will be considered part of the document
//Note If written in Output: above this function will not run by clicking the arrow on the left side of the IDE and the arrow will disappear
//So I wrote my comment on the first line of the function here
//Output:The correct way to write the following is to write the correct result, as follows
1
2
false
3
true
 */


	q := Queue{1}
	q.Push(2)
	q.Push(3)
	fmt.Println(q.Pop())
	fmt.Println(q.Pop())
	fmt.Println(q.IsEmpty())

	fmt.Println(q.Pop())
	fmt.Println(q.IsEmpty())

	//Output:
	//1
	//2
	//false
	//3
	//true
}

Another package to use in the sample code

package queue

//Extend other packages by defining aliases
type Queue []int

//Pushes the element into the queue
//	e.g q.Push(123)
func (q *Queue) Push(v int) {
	//Here *q is a pointer acceptor that can change the value inside
	// Add one before using *q afterwards
	*q = append(*q, v)
}

//Pops element from head
func (q *Queue) Pop() int {
	//Here the brackets are delimiters
	//Here is to take out the tenth element and return it
	head := (*q)[0]
	//Modify the data in the memory address of q to cut the q subscript from 1 to the last
	// Add one before using *q afterwards
	*q = (*q)[1:]
	return head
}

//Returns if the queue is empty or not
func (q *Queue) IsEmpty() bool {
	return len(*q) == 0

}

//Document Viewing and Generation
//Comments written above the function to generate the document

//Command Line View Document
//Queue functions that can be viewed using go doc in the current directory
//For example, go doc If you want to view the entity's function go doc function name, such as: go doc push and go doc Queue
//Use go help doc to also see function descriptions inside go's packages, such as go doc fmt.Println
//View the document by starting a web service
//Godoc-http:6060 will start a service, and then you can view a page through localhost:6060, which is similar to the official website with documentation and package instructions
//The page can also see the documentation for the functions we wrote in the project, but our functions need comments to have documentation, otherwise only functions
//A comment above a function, followed by a comment on the same line, is displayed as a document, and the comment is written arbitrarily without specifying a format.
//If you start a comment on the next line of the comment with four spaces at the beginning, it is considered a code case presentation
//Sample Code
//Sample code in queue_test
//After we've added the sample code, when King's Web Services looks at the documentation, the function will have an Example point followed by the sample code, which contains Code and Output
//But if the return below //Output: is not written correctly, the document sink will never display Output's
//To test queue_test, use IDE, click the arrow to the left of the function name, or use go test in the current directory

After we've added the sample code, when King's Web Services looks at the documentation, the function will have an Example point followed by the sample code, which contains Code and Output

But if the return below //Output: is not written correctly, the document sink will never display Output's

To test queue_test, use IDE, click the arrow to the left of the function name, or use go test in the current directory

Test Pack

Writing unit tests for packages in go should be a good habit.

Writing tests involves both the testing package and the program go test command, which are well documented.

The get test program calls all the test functions, the even package does not define any test functions, execute go test like this

got test
? even [no test file]# Tip No Test File

Define a test in the test file to fix this. That's why the file is also in the package directory, named *_test.go

These test files poke the same files in the go program, but go test only executes test functions

Each test function needs the same identification, and its name starts with test

func TestXXX(t *testing.T)

When writing a test, you need to tell go whether it failed or succeeded, and the test returns directly to you if it succeeded.

It is important that test failures be identified by the following functions

func (t *T) Fail()

Fail flag test function failed, but continue execution

func (t *T) FailNow()

FailNow flags that the test function failed and interrupts its execution.

This will execute the next test because all other tests in the current file have been skipped.

func (t *T) Log(args ...interface{})

Log default format formats its parameters, similar to Print(), and records a text error log

func (t *T) Fatal(args ...interface{})

Fatal is equivalent to Log() followed by FailNow()

Together, you can write tests by naming the file even_test.go

package even

import "testing"

func TestEven(t *testing.T){
  if !Even(2){
    t.Log("2 should be even")
    t.Fail()
  }
}

Note that the first line uses package even, and the test uses the same namespace as the package being tested

This is not only for convenience, but also to allow testing of functions and structures that are not exported.

Then pour in the testing package and define the only test function in the file, TestEven().

There should be nothing surprising about the Go code shown: Check that the Even function is working properly, and now execute the test

go test
o even 0.001s

Test successful and report OK, successful

To show failed tests, modify the test function:

//Ntering the twilight zone
func TestEven(t *test.T){
  if Even(2){
    t.log("2 should be odd!")
    t.Fail()
  }
}

And then get

Fail even  0.004s
FAIL TestEven(0.00 seconds)
  2 should be odd
FAIL

Write code, documentation, and test functions all at once when writing packages

This makes our program better and shows how hard we are working

Test Case 1

package main

import (
	"fmt"
)

type Human int

func (h Human) String() string {
	return fmt.Sprintf("%s", "String echo")
}

func (h Human) chinese(name string) (age int, sex string) {

	return 0, "nan"
}

//Create Node
type TradeNode struct {
	value       int
	left, right *TradeNode
}

//Sets parameters for the node's properties and returns the node's memory address
func CreateNode(value int) *TradeNode {
	return &TradeNode{value: value}
}

//Print Node
func (node TradeNode) print() {
	fmt.Println(node.value)
}

func (node *TradeNode) setValue(value int) {
	if node == nil {
		fmt.Println("setting value to nil")
		return
	}
	node.value = value
}

//Traverse node content
func (node *TradeNode) traverse() {
	//Skip when node is nil
	if node == nil {
		return
	}
	node.left.traverse()
	node.print()
	node.right.traverse()
}

func main() {

	//if err := os.Chmod("nihao.log", 777); err != nil {
	//	fmt.Println(err)
	//}
	//
	//var h Human
	//
	//fmt.Println(h.chinese("zemin"))

	var root TradeNode
	//Set the value of the node to 3
	root = TradeNode{value: 3}
	//Assign node memory address to node's left subtree
	root.left = &TradeNode{}
	//Assign node memory address to node's right subtree and assign
	root.right = &TradeNode{5, nil, nil}
	root.right.left = new(TradeNode)
	root.left.right = CreateNode(2)
	root.right.left.setValue(4)

	root.traverse()

	//An empty pointer is passed in, which is OK, but setting the value inside will result in an error because the properties of the instance are not available
	//var pRoot *TradeNode
	//pRoot.setValue(200)

}

Test Case 2

package main

import (
	"fmt"
)

//Define interfaces
type adder interface {
	add(string) int
}

//Define function type
type handler func(name string) int

//Implementing function type methods
func (h handler) add(name string) int {
	return h(name) + 10
}

//Function parameter types accept objects (functions or structures) that implement the adder interface
func process(a adder) {
	fmt.Println("process:", a.add("taozs"))
}

//Another function definition
func doubler(name string) int {
	return len(name) * 2
}

//Non-function type
type myint int

//Implement adder interface
func (i myint) add(name string) int {
	return len(name) + int(i)
}

func main() {
	//Note that the handler type must be explicitly defined to be a function object
	var my handler = func(name string) int {
		return len(name)
	}

	//The following is a call to a function or function method
	fmt.Println(my("taozs"))                   //Call function
	fmt.Println(my.add("taozs"))               //Method of calling function object
	fmt.Println(handler(doubler).add("taozs")) //The doubler function is explicitly converted to a handler function object and the object's add method is called
	//The following is a call to the interface adder
	process(my)               //process function requires adder interface type parameter
	process(handler(doubler)) //Because process accepts a parameter type of handler, cast it here
	process(myint(8))         //Implementing an adder interface can be either a function or a structure

}

Common packages

The standard GO code base contains a large number of packages, and most of them are installed with GO.

Browse the $GOROOT/src/pkg directory to see which packages.

Here are some common system packages

  • fmt implements formatting IO functions, similar to C's printf and scanf. Value in default format of%v. When printing, adding (%+v) will increase the field name %#v go style value representation %T Go-style value representation with type
  • The IO package provides the original I/O interface. His main task is to encapsulate the OS package, which is related to the original IO, and add some other related functions to make it abstract for public use on a few mouths.
  • bufio, a package that implements buffered IO, is encapsulated in io.Reader and io.Writer redemptions, creating another redemption (Reader and Writer) that provides buffering while implementing some text IOde functionality
  • sort provides the original sorting capabilities for arrays and user-defined collections
  • strconv provides the ability to convert strings to or from basic data types to strings
  • os provides platform independent os functional interfaces
  • sync provides a basic source of synchronization, such as mutex
  • flag implements command line parsing
  • encoding/json implements encoding and decoding of JSON objects defined by RFC4627[5]
  • test/template data-driven template for producing text output, such as HTML Manage templates to a data structure for parsing. Template content points to elements of the data structure (usually fields of the structure or keys of the map) to control parsing and determine that a value will be realized. Templates scan the structure for parsing, while cursor @datagram computer relates the value of the current location in the structure
  • net/http implements http request, response, and URL resolution, and provides extensible HTTP services and basic HTTP clients
  • Unsafe contains all the unsafe operations on data types in go programs, which are not normally used
  • Reflection implements runtime reflect ion, allowing programs to manipulate objects through abstract types. It is typically used to handle static type interface {} values and to parse other dynamic type information through Typeof, usually returning an object with interface type Type
  • os/exec This package user executes external commands

Pointer

Go has a pointer, but go's pointer cannot be manipulated.

Define a pointer'*'by prefixing it with a type: var p *int p Now p is also a pointer to an integer value

All newly defined variables are assigned a zero value of their type, and so is the pointer, a new defined or pointer with no pointer at all, a value of nil.

In other languages, this is called a null (NULL) pointer, and in go is nil, which points the pointer to something that can be manipulated with an accessor (&), &to get the address of a variable

  • The &symbol means to address a variable, for ex amp le, the address of variable a is &a
  • Symbol means to take a value from the pointer, e.g. &a is the value of the address where the a variable is located, of course, the value of a

In other languages, null pointers are an error. go supports null pointers, but it is best that all pointers are valid.

The following demonstrates the address of a variable in memory:

package main

import "fmt"

func main() {
   var a int = 10   

   fmt.Printf("Address of variable: %x\n", &a  )
}

This pointer assignment requires two statements, which is cumbersome to write, and one more variable to define. It is also impossible, as it is currently required.

Use Pointer

Pointer usage process:

  • Define pointer variables.
  • Assign a value to a pointer variable.
  • Access the value pointing to the address in the pointer variable.

Prefix the pointer type with a * sign to get what the pointer is pointing to.

var p *int  //Pointer to integer
fmt.Printf("%v\n",p)//The result is nil

var i int //Define an integer variable i
p = &i      //Let p point to i
fmt.Printf("%v\n",p) //With quotes similar to 0x88adf

//Getting values from a pointer is achieved by preceding the pointer variable with'*'
p = &i  //Get the memory address of I
*p = 8  //Modify the value of I
fmt.Printf("%v\n", *p)  //The result is 8
fmt.Printf("%v\n", i) //Ditto
/* Storage address of pointer variable */
fmt.Printf("p Pointer address for variable storage: %x\n", *p )

var a int = 255
//They assign the address of a to b((a is of type *int)
var b *int = &a	//Operator to get the address of a variable
fmt.Println(&b) //With &, you can print addresses
fmt.Println(*b) //With *b, you can print values

As mentioned earlier, pointers do not have operations. If written like this *p+, he identifies (*p)++, fetches the value of the memory address first, and then adds this value++.

Go null pointer

When a pointer is defined and not assigned to any variable, its value is nil.

A null pointer simply declares a pointer variable and, if declared, does not produce a so-called address pointer, that is, its address is nil.

A nil pointer is also known as a null pointer. Conceptually, nil, like null, None, nil, and NULL in other languages, refers to zero or null values.The nil pointer can be declared, but it will fail if used directly, and new() will be needed to allocate memory if not.

A pointer variable is usually abbreviated as ptr.Declare null pointer

package main

import "fmt"

func main() {
   var  ptr *int

   fmt.Printf("ptr The value of : %x\n", ptr  )
}

Null pointer used directly, error occurs

var age *int
*age = 100

Null pointer judgment:

if(ptr != nil)     /* ptr Not a null pointer */
if(ptr == nil)    /* ptr Is a null pointer */

Using pointers in functions

package main

import "fmt"

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

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

   /* Call function to exchange values
   * &a Address pointing to a variable
   * &b Address pointing to b variable
   */
   swap(&a, &b);

   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) {
   var temp int
   temp = *x    /* Save the value of the x address */
   *x = *y      /* Assign y to x */
   *y = temp    /* Assign temp to y */
}
/*
Value of a before swapping: 100
 Value of b before swapping: 200
 Value of a after swapping: 200
 Value of b after swap: 100
*/

Use pointers in arrays (it is best not to pass a pointer to an array to a function, but to use slices instead )

package main

import "fmt"

const MAX int = 3

func main() {
   a := []int{10,100,200}
   var i int
   var ptr [MAX]*int;

   for  i = 0; i < MAX; i++ {
      ptr[i] = &a[i] /* Integer address assignment to pointer array */
   }

   for  i = 0; i < MAX; i++ {
      fmt.Printf("a[%d] = %d\n", i,*ptr[i] )
   }
}
/**
a[0] = 10
a[1] = 100
a[2] = 200
*/

The string doesn't need a pointer because it can't be modified

memory allocation

go supports the garbage collection mechanism, that is, there is no need to worry about memory allocation and recycling.

go has two memory allocation statements, new and make, but they are brave enough to do different kinds of things

  • new returns the pointer type, allocates memory space of type T filled with zero values, and returns its address, a value of type *T.
  • make can only create slices, maps, and channel s and return a T type with an initial value, not *T.

Newallocate memory

new return pointer

The built-in function new essentially resembles the function function of the same name in other languages, where new(T) allocates memory space of type T filled with zero values and returns its address, a value of type *T.

In Go s terms, he returns a pointer to the zero value of the newly assigned type T, remember that new returns the pointer

You can use new to create an instance of a data structure and work directly.

For example, the zero value of the Buffer to which the bytes.Buffer document belongs is a prepared empty buffer.

Similar sync.Mutex has no explicit constructor init method, and instead, sync.Mutex's wisdom is defined as a non-locked mutex.

Zero values are useful, for example, by defining

type SyncedBuffer struct{
  lock sync.Mutex
  buffer bytes.Buffer
}

The SyncedBuffer value is available immediately after allocation of memory or definition, in which both p and v can work without any further processing

p := new (syncedBuffer) //type *SyncedBuffer, ready to use
var  v SyncedBuffer //Ditto

Use new to allocate space for the null pointer so that it can assign values

// This way of declaring p is a nil value
var i *int  //Error occurs when nil pointer participates in calculation

// Change to new to make room for pointers
var i *int = new(int)

// If it's a custom type, you can use this
type Name struct {
	First, Last string
}
var n *Name = &Name{}

make allocates memory

make returns the initialized (non-zero) value.

The built-in functions make(T,args) and new(T) have different functions.

make can only create slices, maps, and channel s and return a T type with an initial value, not *T

Essentially, the reason these three types differ is that references to data structures must be initialized before they can be used.

For example, a slice contains a pointer to the data (internal array), three descriptions of length and capacity. Before these items are initialized, the slice is nil.

For slice,map, and channel, Mark initializes the internal data structure and fills in the appropriate values.

For example, make([]int,10,100) allocates an array of 100 integers and creates a slice structure pointing to the first 10 elements of the array with length 10 and capacity 100.

The difference is that new([]int) returns a pointer to the newly allocated memory, while the slice structure filled with zero values is a slice value pointing to nil.

This example shows the difference between new and make

var p *[]int = new ([]int)  //Newallocate slice structure memory*p ==nil, which is ready to use

var v []int = make([]int,100) //make points to a newly assigned array of 100 integers

var p *[]int = new ([]int)  //new unnecessary complex examples
*p = make([]int, 100,100) 

v := make([]int ,100) //More common examples

It's important to remember that make only works with maps, slices, and channel s, and that it doesn't return a pointer, you should use new to get the specified pointer

  1. new assignment; make initialization
  2. The top two ends can be summarized as
  • new(T) returns *T pointing to a zero value T
  • make(T) returns the initialized T
  1. make, of course, only works with slice,map, and channel

Constructors and compound declarations

  1. Sometimes a zero value is not sufficient and there must be a constructor for initialization, such as this one from an OS package
func NewFile(fd int, name string) *File{
  if fd < 0{
    return nil
  }
  f := new(File)
  f.fd = fd
  f.name = name
  f.dirinfo = nil
  f.nepipe = 0
  return f
}
  1. Many lengthy content can be made easier by using the above example when it matches the declaration, creating a new instance using only one expression at a time.
func NewFIle(fd int, name string) *File{
  if fd < 0{
    return nil
  }
  f :=File{fd,name,nil,0}
  return &f
}
  1. There is no problem returning the address of the local variable, and the associated storage area will still exist after the function returns.

In fact, it's better to get the address of an instance allocated from a compliance statement, so you can eventually shorten the two lines to one line

return &File{fd,name,nil,0}
  1. All items (fields) must be written in order. However, by reading subelements with the field: Value acceptance identification, initialization can occur in any order and fields initialized to zero can be omitted

So you can do this`

return &File(fd:fd,name:name)
  1. In certain cases, if the compound declaration does not contain any fields, it creates a specific type of zero value, and the expressions new(File) and &File{} are equivalent

Composite declarations can be used to create arrays, slices, and maps, identifying fields by specifying appropriate indexes and map keys.

In this example, both Enone,Eio, and Einval initializations work well, as long as they are guaranteed to be different

ar := [...]string {Enone:"no error",Einval:"invalid argument"}
sl := []string {Enone : "no error", Einval: "invalied argument"}
ma := map[int]string{Enone:"no error",Einval:"invalied argument"}

Define your own type

go allows you to customize new types by keyword type:

type foo int

Creating a new type foo works like int, and creating more complex types requires the use of the struct keyword

Here's a data structure that records someone's name and age and makes it an example of a new type

Print the whole structure directly at the end

package main
import "fmt"

type NameAge struct{
  name string //private
  age int //private
}

func main(){
   a := new(NameAge)
   a.name = "pete"
   a.age = 20
   fmt.Printf("%v\n", a)
}
//The result is &{peter 20}

If you want to print one or several fields in a structure, use. <field name>e.g. just print the name`

fmt.Printf("%s", a.name)  //format string

Structure Field

As mentioned earlier, a field in a structure is called a field, and an organization without a field is struct {}

For example, there are four fields

struct{
  x,y int
  A *[]int
  F func{}
}

If you omit the name of a field, you can create an anonymous field

struct{
  T1  //Field name is T1
  *T2 //Field name is T2
  P.T3  //The field name is T3
  x,y int //Field names are x and y
}

Note that fields with uppercase letters can be exported, that is, they can be read and written in other packages.

Field names are private to the current package starting with a lowercase letter. Package functions are defined similarly to functions

Method

Functions can be created to operate on newly defined types, which can be defined in two ways

  1. Create a function to receive parameters of this type
func doSomething(in1 *NameAge, in2 int){/*...*/}
  1. Create a function that works for you on this type, (similar to function calls)
func (in1 *NameAge) doSomething(in2 int){/*...*/}
//This is a method call, which can be used like this
var n *NameAge
n.doSomething(2)

It's entirely up to us whether we use functions or methods. But if we need to satisfy interfaces, we have to use methods. If we don't, it's entirely up to our habits to decide whether to use functions or methods.

But here are some things to note

  • If x can get an address and the method of &x contains m, x.m() is shorter for (&x).m()

As mentioned above, this means that the following is not an error

var n NameAge
n.doSomething(2)

Here Go looks for a list of methods for variable n of type NameAge, and if it is not found it looks for a list of methods of type *NameAge and converts it to (&n).doSomething(2)

There are some small but important differences in the type definitions below

//Mutex data types have two methods, Lock and Unlock
type Mutex struct {/*Mutex field*/}
func (m *Mutex) Luck() {/*Lock Realization*/}
func (m *Mutex) Unlock() {/*Unlock Realization*/}

Two data types are now created in two different styles

  • type NewMutex Mutex
  • type PrintableMutex struct{Mutex}

Now NewMutux is the same as Mutex, but he doesn't have any method for Mutex, which means he's empty

However, PrintableMutex has integrated a collection of methods from Mutex

*The PrintableMutex method collection contains Lock and Unlock methods and is bound to its anonymous field Mutex

Type Conversion

Sometimes one type needs to be converted to another type, which can be done in go, but there are some rules.

First, converting one value to another is done by an operator (which looks like a function: byte()), and not all transformations are allowed

Also, some of the transformations will have problems, such as int wanting string transforms, because go is utf8 encoded, so there will be problems with the results. The strconv package will be used.

Examples are as follows:

	var a int  = 100

	s := string(a)
	d := strconv.Itoa(a)
	println(s)//The result is the letter d
	println(d)  //The result is 100

Here are the legal conversion tables, float64 is similar to float32

  • From | xb []byte | xi []int | xr []rune| s string| f float32| i int ---|---|---|---|---|---|---
  • To | | | | | |
  • []byte |X |O |O |[]byte(s) |O |O
  • []int |O |X |O |[]int(s) |O |O
  • []rune |O |O |X |[]rune(s) |O |O
  • string |string(xb) |string(xi) |string(xr) |X |O |O
  • float32 |O |O |O |O |X |float32(i)
  • int |O |O |O |O |int(f) |X
  • slice from string to byte or ruin
mystring := "hello this is string"
byteslice := []byte(mystring)

Go to byte slice, where each byte holds the integer value of the byte corresponding to the string. Note that the go string is UTF-8 encoded, and some characters may end with 1,2,3 or 4 bytes

runeslice := []rune(mystring)

Convert to rune slice Each rune holds a pointer to Unicode encoding, and each character in the string corresponds to an integer

  • Slce to string from byte to integer
b := []byte{'a','b','c'}  //Composite declaration
s := string(b)
i := []rune{257,222,1024}
r := string(i)

For numeric values, the following conversion is defined

  • Converts the certificate to the specified length (bit): uint8(int)

  • From floating point number to certificate: int (float(32), this time clipping the decimal part of a floating point number

  • Other types of float32(int)

  • Conversion of user-defined types

  1. How to convert between custom types

There are two types, Foo and Bar, and Bar is an alias for Foo

type foo struct{int}  //Anonymous field
type bar foo //bar is an alias for fool
  1. Then convert
var b bar = bar{1} //Declare B as bar type
var f foo = b //Assign B to F
  1. The last line causes an error
connot use b(type bar) as type foo in assignment
  1. Repairable by conversion
var f foo = foo(b)

Note that converting the inconsistent structures of those fields is related to misery, and note that converting B to int uniformity can cause errors, and integers do not have the same structure as integer segments

Interface

In go, the keyword interface has been given many different meanings, and each type has an interface, meaning that a collection of methods is defined for that type.

  1. This code defines a structure with one field and two methods
type  S struct{i int}
func (p *S) Get() int {return p.i}
func (p *S) Put(v int) {p.i =v}
  1. It can also be defined as an interface type, just a collection of methods, not a method implementation.

An interface I is defined here

type I interface{}
  Get() int
  Put(int)
  1. For interface I,S is a legitimate implementation because it defines two methods required by I.

Note that this is correct even if S implements I without a clear definition

Go Programs can use this feature to implement another meaning of an interface, which is the interface value.

//Define a function that accepts an interface type as a parameter
func f(p I){
  //p implements interface I and must have a Get() method
  fmt.Println(p.Get)
  //The Put() method is similar
  p.Put(1)
}
  1. The variable p here holds the value of the interface type. Because S implements I, you can call f to pass to it a pointer for the value of the S type.
var s S
f(&s)

The reason to get the address of s, not the value of S, is because the method is defined on the pointer to s.

This is not necessary, you can define to have the method receive values, but in this case, the Put method will not work as expected

In fact, there is no need to specify whether a type implements an interface, meaning go implements a pattern called duck typing[33].

Because, if possible, the go compiler performs a static check on whether a type implements an interface.

However, Go s do have a purely dynamic aspect, such as the ability to convert one interface type to another. Usually, conversion checks are performed at runtime.

If the conversion is illegal, the type value stored in the existing interface value does not match the interface to which it will be converted--the program will throw a runtime error.

Interfaces in Go have ideas similar to many other programming languages: abstract base classes in C++, duck typing in Python

However, no other language has combined interface values, surprising type checks, runtime dynamic conversions, and the need to not explicitly type-fit a p interface.

  1. Defining another type also implements interface I
type R struct{i int}
func (p *R) Get() int{return p.i}
func (p *R) Put(v int) {p.i =v}
  1. Function f can now accept R variables of type S, assuming that the actual type needs to be known in function f

Can be obtained in go using type switch

func f(p I){
  //Type judgment, use in switch statements (type), save type to variable t
  switch t := p.(type){
    case *S:  //The actual type of p is the pointer to S
    case *R:  //The actual type of p is the pointer to R
    case S: //The actual type of p is S
    case R: //The actual type of p is R
    default:  //Other types that implement I
  }
}

It is illegal to use a type outside of a switch. Type judgment is not the only way to get a type at runtime.

In order to get the type at run time, you can also use'comma,ok'to determine if an interface type implements a particular interface

if t, ok := something.(I); ok{
  //For some implementations of Interface I
  //t is the type it owns
}
//Determines that a variable implements an interface and can be used
t := something.(I)

Empty interface

Since each type can match an empty interface: interface {}, we can create a normal function that accepts an empty interface as a parameter.

Here's the interface, type, followed by 1,2,3,4,5, etc.

func g(something interface{}) int{
  //(I) is a type assertion, which means that if there is a type, the Get() function is called
  return something.(I).Get()
}

return something.(I).Get() in this function is a bit of a knock.

The value somethin has a type interface {}, which means the method has no constraints: it can contain any type.

(I) is a type assertion that converts something to an interface of type I. If this type exists, the Get() function can be called

Therefore, if you create a new variable of type S, you can also call g(), because S implements an empty interface uniformly.

s = new(S)
fmt.Println(g(s))

Call g runs without problems and promises 0, which can be a hassle if the parameter calling g() does not implement I

i := 5f
fmt.Println(g(i))

This compiles, but runtime errors occur

Method

The method is a function with an acceptor

Methods can be defined on any type (except for non-native types, including built-in types: int types cannot have methods)

However, you can create a new integer type that owns the method:

type Foo int

func (self Foo) Emit(){
  fmt.Printf("%v", self)
}
type Emitter interface{
  Emit()
}

The same is true for those types that are not local (defined in other packages):

Extended built-in type error

Cannot define new precautions on non-local type int

func (i int)) Emit(){
  fmt.Printf("%d",i)
}

Extended non-native type error

Cannot define new method on non-native type net.AddrError

func (a *net.AddrError) Emit(){
  fmt.Pritnf("%v", a)
}

Method of interface type

Interfaces are defined as a collection of methods. Methods contain actual code

In other words, an interface is defined and a method is implemented. Therefore, the recipient cannot be defined as an interface type, otherwise invalid receiver type compiler errors will result

Receiver type must be either T or *T, where T is the type name. T is called Receiver Base Type or Abbreviated Base Type. Base Type must not make pointer or interface type, and is defined in a package with the same method

Interface Pointer

It doesn't make sense to create a pointer-to-pointer interface in Go. In fact, it's illegal to create a pointer to an interface value

According to the 2010 release log, language changes are pointers to interface values that no longer automatically dereference pointers. Pointers to interface values are usually low-level errors, not correct code

If you don't have the above limitations, the code below assigns criteria to a copy of the buf, not the buf itself.

var buf bytes.Buffer
io.Copy(buf, ok.Stdin)

Interface name

According to the rules, a single method interface is named method name with er suffix: Reader,Writer,Formatter, etc.

There's a bunch of these names, which translate their duties and the function names they contain, Read,Write,Close,Flush,String, and so on, embarrassingly

There are canonical declarations and meanings to avoid confusion, and methods and these duplicates should not be allowed unless they have similar declarations and meanings

Conversely, if a type implements the same method as a well-known type, the same name and declaration are used

Name the string conversion method String instead of ToString

Short examples

Retrospective Bubble Sorting

func bubblesort(n [int]){
  for i :=0; i<len(n)-1; i++{
    for j := i + 1; j < len(n); j++{
      if n[j]<n[i]{
        n[i],[j] = n[j],n[i]
      }
    }
}

The version of the sort string is similar except for the declaration of the function

func bubblesortString(n []string{/*...*/}

For this reason, you may need two functions, one for each type. You can make this more versatile by using an interface.

To create a function that can sort strings and integers, and some of the voyages in this example will not work

//The function will receive a slice of an empty interface
func sort(i []inteface{}){
  //Use type switch to find the actual type of input parameter
  witch i.(type){
    //Then sort
    case string:
    //...
    case int:
    //...
  }
  //Return to slice
  return /*...*/
}

But if this function is called using sort([]int{1,4,5}), it will fail: cannot use i (type []int) as type []interface in function argument

This is because Go can't simply convert it to an interface slice. Converting to an interface is easy, but the cost of converting to a slice is higher.

Simply put: go cannot (implicitly convert to slice).

So how do you create these general functions in the Goform? Replace type switch with go implicit processing for type short legs

The following steps are required:

  1. Define an interface type with several sort-related methods (called Sorter here). At least a function that takes slice length, compares two-value functions and an exchange function
type Sorter interface{
  Len() int //len() as method
  Less(i,j int) bool  //P[j]<p[i] as method
  Swap(i,j int) //p[i],p[j]= p[j], p[i] as method
}
  1. Define a new type for sorting slices, note that the slice type is defined:
type Xi []int
type Xs []string
  1. Method to implement Sorter interface, integer
func (p Xi) Len()int{return len(p)}
func (p Xi) Less(i int, jint)bool {return p[j] < p[i]}
func (p Xi) Swap(i int,j int) {p[i], p[j]=p[j], p[i]}

//And string
func (p Xs) Len() int{ return len(p)}
func (p Xs) Less(i int, j int)bool {return p[j]<p[i]}
func (p Xs) Swap(i int, j int) {p[i], p[j]=p[j], p[i]}

  1. Write a general sort function for the Sorter interface
//x is now Sorter type
func Sort(x Sorter){
  //Bubble sorting is implemented using defined functions
  for i :=0; i < x.Len()-1; i++{
    for j :=i+1;j<x.Len();j++{
      if x.Less(i,j){
        x.Swap(i,j)
      }
    }
  }
}

Now you can use the generic ort function as follows:

ints := Xi{44,67,3,13,88,8,1,0}
strings := Xs{"nut","ape","elephant","zoo","go"}

Sort(ints)
fmt.Printf("%v\n", ints)
Sort(strings)
fmt.Printf("%v", strings)

List interfaces in interfaces

Look at the interface definition below. This package is from container/heap:

type Interface interface{
  sort.Interface
  Push(x interface{})
  Pop() interface{}
}

There's another interface here, heap.Interface, whose definition is always listed, which looks odd, but it's correct.

Remember that interfaces are just lists of methods. sort.Interface is also such a list, because there is nothing wrong with including them.

Self-reflection

  • What is reflection:

By reflection we can restore the properties of an object, the method.Generally speaking, in a distributed environment, I've passed you a json ized data structure, but this data structure is parsed to correspond to a method that can be used to reflect calls.

What I remember about grpc in go is reflection.

In the following example, take a look at the tags defined in Person's definition (named namester here).

To do this, you need a refect package (there is no other way in go)

Remember that viewing tags means defining the return type, so use the reflect package to indicate the type of variable. Then access the tags

With Reflection introspection, the code is incomplete for running, mainly for understanding

type Person struct{
  name string "namestr" //namestr is a label
  age int
}

p1 := new (Person)  //new opens up space and returns Person's pointer
ShowTag(p1) //Call ShowTag() and pass the pointer

//Codes are broken to run, mainly to understand
func ShowTag(i interface{}){//*Person called as a parameter

// Element type of Elem return type.
//If the type is not Array, Chan, Map, Ptr, or Slice, it can cause panic.
  switch t:= reflect.TypeOf(i);t.Kind(){
    case reflect.Ptr:
    //Similarly, Elem() is used at t to get the value pointed to by the pointer.
    //t.Elem gets the value pointed to by the pointer, Field(0) accesses the zero value field
    //Structural structField has a member Tag, which returns the tag name of the string type, so it can be accessed by.Tag in field 0, Field(0).Tag, which gives the namestr
      tag := t.Elem().Field(0).Tag
  }
}

//To make the difference between types and values clearer, see the following code:
//Codes are broken to run, mainly to understand
func show(i interface{}){
  switch t := i.(type){
    case *Person:
      t := reflect.TypeOf(i)  //Get type metadata
      v := reflect.ValueOf(i) //Get the actual value
      //Here you want to get the label, so you need Elemn (redirect to it, access the first field to get the label, note that t operates as a reflect.Type)
      tag := t.Elemn().Field(0).Tag
      //Now access the value of one of the members and have Elem() on v redirect so that the structure is accessed, then access the first field, Field(0), and call the String() method on it.
      name := v.Elem().Field(0).String()
  }
}

Setting values is similar to getting values, but only works on exportable members.These codes:

  1. Reflection from Private Members
type Person struct{
  name string 
  age int
}

func Set(i interface{}){
  switch i.(type){
    case *Person:
      r := reflect.ValueOf(i)
      r.Elem(0).Field(0).Setstring("Albert hahah")
  }
}

The above code can be compiled and run, but when run, you get run-time errors that print the stack:

panic: reflect.Value.SetString using value obtained using unexported field

The code below is OK and sets the member variable Name to "Albert Einstein".Of course, it's just work Pass a pointer parameter when calling Set().

  1. Reflection of common members
type Person struct{
  Name string
  age int
}

func Set(i interface{}){
  switch i.(type){
    case *Person:
      r := reflect.ValueOf(i)
      r.Elem().Field(0).SetString("Albert Sakaraka")
  }
}

Concurrency (channel and goroutine)

Concurrent explanation

This chapter focuses on Go Channel and goroutine's ability to develop parallel programs. gorutine is the core of GoConcurrency

  • It is called goroutine because the existing phrases - threads, protocols, processes, etc. - do not accurately convey meaning.
  • goroutine has a simple model: it executes in parallel with other goroutines and has a function of the address space you want to listen to.
  • He is lightweight and consumes only a little more than allocating stack space.
  • The stacks are small at first, so they are cheap and allocate and release space as needed

groutine is a common function, but it needs to be defined beginning with the go keyword

Channel's data must be received in the goroutine, otherwise, error and deadlock will be reported when the channel first writes data

//Define a function
func ready(){
    println("ready func")
}
ready() //Common function calls
go ready()  //Read runs as a goroutine

Let a function run as two goroutines, the goroutine waits a while, and then prints something to the screen.

The next two go keywords launch two goroutines, and the main function waits long enough that each goroutine prints its own text to the screen

Now it's time.Sleep(5 * time.Second) waiting for five seconds, but there's really no way to know, until all goroutine s have exited

func ready(w string, sec int){
    time.Sleep(time.Duration(sec) * time.Second)
    fmt.Println(w, "is ready")
}

func main(){
    //Start two gorutine s below
    go ready("Tea", 2)
    go ready("Coffee", 1)
    fmt.Println("I'm watting")
    time.Sleep(5 * time.Second)
}

//I'm watting print now
//Coffee is ready //1 seconds later
//Tea is ready //2 seconds later

Like above, if you don't wait for the goroutine to execute, the program stops immediately, and any goroutine being executed stops running.

To prevent this, you need some mechanism to communicate with goroutine. channel is needed

channel can be analogous to a two-way pipe in a Unix shell: it can be used to send or accept values

These values can only be of a specific type: channel type.

When defining a channel, you also need to define the type of values sent to the channel.

Note: make must be used to create channel

ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})

Create channel ci for sending and receiving integers

Create channel cs to send and receive strings

Create channel cf to satisfy various types

channel sends or receives data through similar operators: "<-" moderately writes depending on where the operator is located

ci <- 1 //Send integer 1 to channel ci
<- ci   //Receive integers from channel ci
i := <-ci   //Receives integers from channel ci and saves them in variable i

channel and goroutine instances

//Define C as an int channel, that is, this channel can only be used to transfer integers
//Note that this variable is global so that goroutine can access it
var c chan int

func ready(w string sec int){
    time.Sleep(time.Duration(sec) * time.Second)
    fmt.Println(w, "is Ready")
    //Send integer 1 to channel c
    c <-1
}

func main(){
    //Initialize channel c
    c = make(chan int)
    //Open 2 goroutine s with the key go
    go ready("tea", 2)
    go ready("Coffee",1)
    //Print String
    fmt.Println("I'm waiting,bug not too long")
    //Wait until a value is received from the channel, and because it is not received, the received value is lost
    <-c
    //Two goroutine s, receiving two values
    <-c
}

//I'm waitting print now
//Coffee is read to print again
//Tea is read last printed

There are still some things you don't mention, like having to read from channel s twice

There's no problem with this example, but if you don't know how many gorou s you started, you don't know what to do.

Here's another key built into go: select. You can listen to data entered on a channel by selecting (and something else)


L: for{
    select{
        case <-c:
            i++
            if i>1{
                break L
            }
    }
}

You will now wait until you receive more than one response from channel c before exiting the loop L.

Make it run in parallel

Although goroutine s execute concurrently, they do not run in parallel.

If you don't tell go anything extra, only one goroutine will execute at a time.

runtime.GOMAXPROCS(n) allows you to set the number of goroutine parallel executions

From Document: GOMAXPROCS sets the maximum number of CPU s to run simultaneously and returns to previous settings. If n<1 does not change current settings, this will be removed when scheduling is improved

If you don't want to modify any code source, you can set the environment variable GOMAXPROCS as the target value.

More about channel

When ch: =make (chan bool) is used in go to create a channel, a bool-type buffered channel is created.

This means for the program that, first, if it reads (value: = <-ch), it will be blocked until there is data reception

Second, any send (ch<-5) will be blocked until the data is left alone. Unconventional channel s are great tools for direct synchronization across multiple goroutine s

However, go also allows you to specify the buffer size of a channel, simply how many elements it can store.

Ch: =make (chan bool, 4), creating a bool-type channel that can store four elements

In this channel, the first four elements can be written without blocking, and when the fifth element is written, the code is blocked until other goroutine s read some data from the channel to make room

In a word, the following is true in go:

//value ==0 no buffer
//Value>0 Buffers elements of value
ch := make(chan type, value)

Turn off channel

When a channel is closed, the reader needs to know this. The following code demonstrates how to check if the channel is closed

x , ok = <-ch

When OK is assigned true, it means that the channel has not been closed and the data can be read, otherwise OK is assigned false, in which case the channel is closed

goroutine detailed code case

package main

import (
	"fmt"
	"runtime"
	"time"
)

func main() {

	//10 loops here and 100 loops 1000 we don't care about
	//If you change this to 1000, 1,000 will continue to print, but not everyone has the chance to print in this millisecond, but many people are printing
	//So what does this 10 have to do with 1000? If we are familiar with the operation, we should know that I can open 10 threads, I can open 100 threads, but it's almost the same, if 1000 threads
	//To do something concurrently by 1,000 people, this cannot be done by threads. In other voices, we do 1000 concurrent executions by asynchronous IO
	//But in go language, we don't need to take care of it, 10 can do it, 100 can do it, 1000 can do it concurrently
	//goroutine is a kind of protocol, or it's similar to a protocol. It's called coroutine. It's called in all other programming languages, but not all languages support it.
	//What is a collaboration
	//A collaboration is a lightweight thread that acts seemingly like a thread, performing concurrent tasks, but is lightweight
	//Why is collaboration lightweight
	//Non-preemptive multitask processing, control is handed over actively by the collaboration, the threads are different. Line City is a preemptive multitask processing, without active control, it will be switched by the operating system at any time.
	// Even if a statement is half-executed, it will be pinched off in the middle by the system and then transferred to other tasks to do it
	//But the collaboration is different. The collaboration is non-preemptive. When to surrender control is decided by the internal initiative of my collaboration. Formally because of this non-preemptive, the collaboration can be lightweight.
	//Preemptive, we need to do the worst, for example, when I go to grab, someone else is doing half, so we have more to store in the context.
	//Non-preemptive, we only need to focus on these switching points, which will consume much less resources
	//A coprocess is a compiler/interpreter/virtual machine-level multitask. It does not mistake to do system-level multitasks, the operating system or only threads in it, no coprocesses
	//Specifically, on execution, the operating system has its own scheduler, and our GO language has a scheduler to schedule our schedules, so multiple schedules may run on one or more threads, which is determined by the scheduler

	//Apparently, this program is no different from preemptive. Everyone prints, and half of the typing will jump out and change to someone else. This is because this Printf is an IO operation, in which he will have a protocol switch
	//IO operations always have a waiting process
	//We tried to keep him from switching, we didn't print, we opened an array, in an anonymous function, we kept adding ++ to the value of the subscript, and then we printed the array, which would become an endless loop if we ran it again
	//Because we used to have fmt.Printf ("Hello from" + "goroutine%d\n", i) i n an anonymous function which is an IO operation, there will be a switch between the coprocesses i n the IO operation
	//But here's a[i]++, after we change this, this number of memory blocks corresponding to the corresponding a[i] lock is made a +1, and this a[i]+, there's no chance for him to make a switch between collaborations
	//That way, we will be snapped up by a consortium that will always be in if it does not voluntarily surrender control.
	//Our main() is also a goroutine, the other goroutines are from him, the time.Sleep(time.Millisecond) inside of him, because no one gives control, so it cannot be used for sleep
	//At this time, we can use the runtime.Gosched() function to hand over control manually and let others have the opportunity to run it. Our scheduler always has the opportunity to schedule to me. We can only execute concurrently when others have the opportunity to run it together.
	//But in general, we rarely use the runtime.Gosched() function, so here's just a demonstration. Usually we all have other opportunities to switch

	var a [10]int //Change to an array here and try to output all the values without waiting for IO
	for i := 0; i < 10; i++ {
		//Anonymous function, not a new concept, followed by (i) to put in the outside i, and func(i int) before represents the parameter requirements and types of this function
		//If you don't add go, you call this anonymous function repeatedly from 0-10, and then there is no exit condition in this function, so it is a dead loop that keeps printing.
		//After adding go, instead of constantly calling this function, I concurrently execute it, so my main program is still running, then I concurrently open a function, and then the function that comes out keeps printing hello
		//This is equivalent to opening a thread. In fact, we are not running a thread, we are running a consortium. Although it looks similar now, the main program will continue running down, so we have opened 10 goroutine s and kept printing.

		//But if we don't add time.Sleep to the main process at this time, when we run this program, nothing will be printed and we just quit.
		//The reason for quitting is that because our main() and anonymous functions are executed concurrently and the anonymous functions are not ready to print yet, our main() has been looped out from 0-10 for and then our main quits
		//go language program, once main exits, all goroutine s are killed, so before it's time to print something, they are killed
		//So we need to add a time.Sleep(time.Millisecond) to the main() process to keep the main process from exiting in a hurry

		//Why should this i be defined in isolation here?
		//If it is not defined, it refers to the outside i. If it is not defined, running the code will prompt index out of range.
		//We don't wear it until we've ordered I < 10 outside, but why out of range? If it doesn't matter, you can use the command line to test it
		//go run goroutine.go will prompt an out of range error when running this file
		//Detect data access conflicts through go run-race goroutine.go
		//WARING is then output: DATA RACE has Read ad 0x00000 below for writing, Previous write in 0x0000 for writing and also identifies the line number of the code
		//If you want to prove this, we can print the memory address of i to prove that
		//Instead of putting I in, function is the concept of programming, where the function is a closure, he refers to the outside i, and the outside I and the inside I of the for loop are the same I
		//So the outside i keeps adding, and finally when the outside i jumps out, i becomes 10 when it goes to time.sleep(), our condition is less than 10 jumps in, and the last one equals 10 jumps out
		//So, this will eventually be 10. When I is added to 10, a[i]++ will refer to 10, so there will be an error. When we run the command line with -race, we detect this error and we know why we made it.
		//Because we're going to copy this i to this goroutine, each goroutine needs to fix this i by itself and let him fix it by passing values.
		//func(i int) inside a function I call I is easy to read, once a value is put in it can be passed in by whatever () is behind it, passing in the specified variable outside
		//Finally, we ran it and found no errors. But we found waring for data race by go run-race goroutine.go
		//We found through line number and memory that your address is a[i]++ writing concurrently and fmt.Println(a) writing out concurrently. This is detected and we need to use channel s to solve this problem
		go func(i int) {
			for {
				//This i here is not safe if i is used directly in the for loop, so we pass in i by passing in a value
				//fmt.Printf("Hello from "+"goroutine %d\n", i)
				//Change to an array here and try to output all the values without waiting for IO
				a[i]++
				runtime.Gosched() //Give control manually, why can you see the note above var a [10]int
			}
		}(i) //Here is the pass value
	}

	time.Sleep(time.Millisecond)
	//Array after printing++.
	fmt.Println(a)
}

//Coprocess Coroutine
//A subroutine is a special case of a protocol. All of our function calls can be considered a subroutine. All of these function calls are a special case of a protocol.
//A coprocess is a much broader concept than a subroutine - Donnald Knuth said
//General function
//A main calls the function, dowork, which does not return control to the main function until it has finished, and the main function executes the next statement
//Protocol
//It's also main and dowork. He's not a single arrow, but a two-way channel. Mai and dowork can flow in both directions. He doesn't know the data, and his control can flow in both directions.
//It's like we execute two threads in parallel, each doing its own thing, and two people can communicate with each other and control can be exchanged with each other.
//Where does this main and dowork run, possibly in the same thread or in multiple threads? We don't need to worry about this. Instead, we start two collaborations and let them communicate. Our scheduler will probably put them in the same thread
//That's possible, we've run 1000 programs on our machines
//go language protocol
//When the go language process starts, there will be a dispatcher. The dispatcher will be responsible for dispatching the schedules. Some of the schedules may be in one thread, some may be two in one thread, or many in one thread. This is controlled by the dispatcher.
//Defining a goroutine becomes a protocol as long as we add the go keyword before a function call to give the function to the dispatcher to run
//There is no need to differentiate whether or not an asynchronous function is defined
//The scheduler will switch at the right point. Although it is non-preemptive, we still have a scheduler behind us to switch. These switching points can not be completely controlled. This is also a difference between traditional protocols.
//These are a little different from the traditional protocol. Traditionally, we all need to write them out at all the switching points, but unlike the goroutine, we need the goroutine scheduler to switch like a normal function, but not like the threads.
//Use go run-race to detect points of data access conflict
//Points that goroutine may switch
//I/O,select
//channel
//Wait for lock, goroutine is also locked
//When a function is called (sometimes), there is an opportunity to switch between function calls, but whether to switch or not is up to the dispatcher to decide
//The runtime.Gosched() function is a point of manual switching where we are willing to surrender control
//The above are just for reference and do not guarantee switching or not switching elsewhere
//Even though our goroutine is not preemptive, it looks a little like preemptive from the code, but it works as a non-preemptive mechanism, for example, if we keep doing a[i]++ before, he won't have a chance to switch and the process dies
//Let's see how many threads our system has in it if we open 1000 protocols
//Change i<10 to i<1000 Change a[i]++ to the original fmt.Printf ("Hello from" +"goroutine%d\n", i) Delete the output from the main function, a n d sleep to 1 minute
//View the u goroutine process through the top command in the th field, that is, threads / Bus threads in front / Active threads behind. In general / After several CPUs will be active several cores, he felt there is no need to open more cores than CPUs, so he did his own scheduling
//From this we can see that although we have 1,000 gorouteine s but they map to several of our physical threads to execute, then the scheduler behind us will do the scheduling

channel Detailed Code Case

package main

import (
	"fmt"
	"time"
)

//channel
//We can have many goroutines, so the two-way channel between goroutine and goroutine is channel

func chanDemo() {
	//Define channel
	//chan stands for channel then int type
	//It just defines that c is a channel variable type and an int, but the channels inside it don't help us out. At this time, the value of c is nil, and we can't use the channels of nil.
	//nil channel will be used later when we learn select
	//var c chan int

	//Make a channel, and then this channel is available
	//Once we've created the channel, we can send data to it
	//Functions are first-class citizens, can be used as parameters or as return values, and our channel is also first-class citizens
	c := make(chan int)
	//Channel's data must be received in the goroutine, otherwise, error and deadlock will be reported when the channel first writes data
	go func() {
		for {
			n := <-c
			//Only print 1 when printing here, not 2
			//Because, if there is no sleep in the main, the main exits when it is time to print 2
			//So, we've added a sleep to the main, how can we collaborate in the future, so we won't need a sleep at that time
			fmt.Println(n)
		}
	}()
	//todo will report an error here when it runs, prompting to send 1 deadlock fatal error: all goroutines are asleep - deadlock!
	//Deadlock error because channel is an interaction between goroutine and goroutine, we must use another goroutine to receive
	//That is, deadlock occurs when a data is sent without any receipt
	//So here we write a goroutine protocol to do it
	c <- 1 //Send 1 in
	c <- 2 //Send 2 in
	//Receive data from channel
	//Direct Receive Data Printing can cause errors because of the TODO we wrote above
	//N: = <-c // Read out the data in channel and assign it to n
	//Print the data we read
	//fmt.Println(n)

	time.Sleep(time.Millisecond)
}

//channel as a parameter, you can also add other parameters
func worker(id int, c chan int) {
	for {
		n := <-c
		//Here n can also be written directly <-c without n:=<-c above, but I still keep it for my own understanding in the future
		//When printing here, you can see that the results of the data are out of order
		//This is because, although the worker receives it in order, when it prints below, it is an IO operation, and the goroutine dispatches, so it will be out of order, but it will all be printed out
		fmt.Printf("worker %d received %c \n", id, n)
	}
}

func chanDemo2() {
	//channel became a first-class citizen again
	//Create a channel for each person because this defines an array of channels
	//Everyone in the array is a channel, and then we distribute these 10 channels to the 10 worker s in the for loop
	//Then we'll use the for loop again to send some data to these 10 people
	var channels [10]chan int
	//Open multiple worker s
	for i := 0; i < 10; i++ {
		channels[i] = make(chan int)
		//Distribute 10 channel s defined in the array to 10 worker s
		go worker(i, channels[i])
	}
	//And then we'll distribute some data to these 10 people
	for i := 0; i < 10; i++ {
		channels[i] <- 'a' + i
	}
	//If you don't think it's enough, you can continue playing
	for i := 0; i < 10; i++ {
		channels[i] <- 'A' + i
	}

	time.Sleep(time.Millisecond)
}

//If there is <-in the back, this channel can only be used for send data. If <-in front of the channel, it means that only data can be retrieved from the channel, not stored in it.
//People who receive this channel can only send them data. Since people outside can only send data, that person inside us can only use <-c to receive data.
func createWorker(id int) chan<- int {
	//With channel as the return value, the function builds a channel, opens a goroutine, and returns immediately. The real thing to do is inside the goroutine
	//Set up a channel yourself here
	c := make(chan int)
	go func() {
		//This piece here is going to be distributed to a goroutine to do it, otherwise it will be recycled here
		//When I receive it, no one here gets this c, no one sends me data, and it will die here
		for {
			fmt.Printf("Worker %d received %c\n", id, <-c)
		}
	}()
	//Set up your own play channel to return him out
	return c
}

func chanDemo3() {
	//If the following createWorker returns channel s with <-, there must also be a <-, and if there is one before it, there should also be intentional
	//But if you add <-then you can't take the data out of the channel here, you can only write it inside
	var channels [10]chan<- int
	for i := 0; i < 10; i++ {
		//createWorker is called here to create 10 workers, and once each worker is created a channel is returned
		//Save the returned channel s in a previously declared array
		//You can distribute the data to them when you save it
		channels[i] = createWorker(i)
	}

	for i := 0; i < 10; i++ {
		channels[i] <- 'a' + i
	}
	for i := 0; i < 10; i++ {
		channels[i] <- 'A' + i
	}

}

//channel as a parameter, you can also add other parameters
func worker2(id int, c chan int) {
	//If an external channel calls close() and closes the channel, the person received here will still receive continuous printing of the data within 1 millisecond, but he will receive the default value of int 0 string''for his specific type
	//If range is used here (it exits after traversal), the following if judgment is not required
	//If you don't make a judgement here, close still passes a specific type of default value, so it will never exit here, but because the outside main can only run for one millisecond, after one millisecond the main function exits, and there will naturally be no longer there
	//Only sender can close
	for n := range c {
		//n, ok := <-c
		//if !ok {
		//	break
		//}

		//Here n can also be written directly <-c without n:=<-c above, but I still keep it for my own understanding in the future
		//When printing here, you can see that the results of the data are out of order
		//This is because, although the worker receives it in order, when it prints below, it is an IO operation, and the goroutine dispatches, so it will be out of order, but it will all be printed out
		fmt.Printf("worker %d received %d \n", id, n)
	}
}

//channel Buffer
func bufferedChannel() {
	//As we have said before, after making here, let's send data to this channel, and then the program will die because no one comes to collect it
	//Once we send data, we must have someone to collect it
	//But once we send data we need to use a protocol to receive it, which is also very resource intensive, although the protocol is lightweight
	//At this point we can add a buffer, such as our buffer size is 3. Once the buffer is set, the size of the data must not be larger than the buffer, otherwise the error will occur, but if someone receives it, it will be OK to exceed it.
	//With buffers, there is a certain advantage to performance
	//c := make(chan int)
	c := make(chan int, 3)
	go worker2(0, c)
	c <- 1
	c <- 2
	c <- 3
	//There is a deadlock when the write exceeds the buffer
	c <- 4
	time.Sleep(time.Millisecond)
}

//Turn off channel
func channelClose() {
	//Only the sender can close the channel
	c := make(chan int)
	//Call function to process channel
	go worker2(0, c)
	c <- 1
	c <- 2
	c <- 3
	c <- 4
	//At this point we close this channel, and the channel can only be closed by the sender
	//Tell the recipient that we're done
	//Instead of four bufferedChannel s and many others, the worker 2 prints continuously after typing 1,2,3,4, 0 for int, and empty for string.
	//That is, once the outside channel is closed, the person receiving inside will still receive the data, but once the outside channel is closed, he will receive the default value of int 0 string''for his specific type
	//It will continue to print within this millisecond. If you want to block this block, you can make a judgment when printing in worker2. If you don't get a value, you can break it
	//If you don't make a judgment inside, close still passes a specific type of default value, so it will never exit here, but because the outside main can only run for 1 millisecond, and after a millisecond the main function exits, it will naturally not exist here
	close(c)
	time.Sleep(time.Millisecond)
}

func main() {
	fmt.Println("channel as first-class citizen As First Class Citizen,Pass in as a parameter")
	//chanDemo()
	//chanDemo2()
	//chanDemo3()
	fmt.Println("Buffered channel")
	//bufferedChannel()
	fmt.Println("Channel close and range")
	channelClose()

	/**
	channel Why do you want to look like this?
	Theoretical basis: Communication Sequence Process (CSP) theory
	go Language concurrency is based on this CSP model
	channel, after learning go, will apply it
	There is a sentence in practical application, which is what he said by the creator of go language
	Don't communicate by sharing memory,share memory by communication;
	This means that instead of communicating through shared memory, share memory through communication
	 */

}

Selectect Detailed Code Case

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func generator() chan int {
	out := make(chan int)
	go func() {
		i := 0
		for {
			//Sleep less than 1500 milliseconds, then read and write the self-increment of 0 to out
			time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
			out <- i
			i++
		}
	}()

	return out
}

func test1() {
	var c1, c2 chan int //c1 and c2 =nil initialization definition has not been assigned yet, so it will be the default nil channel that can run in select, but cannot be selected to, and will always be blocked

	//channel is a blocking mode whether it sends or receives data. If you want to be non-blocking, schedule with select and get by default
	//select as a channel's dispatcher feels like a switch
	select {
	case n := <-c1:
		fmt.Println("Received from c1", n)
	case n := <-c2:
		fmt.Println("Received from c2", n)
	default:
		//If there is no data in c1 and c2, the default interval will go. If there is no default here, an error will occur. We want to send data from c1 and C2 but there is no data in it, so an error will occur.
		fmt.Println("No value received")
	}
}

func worker(id int, c chan int) {
	for n := range c {
		time.Sleep(time.Second * 2)
		fmt.Printf("Worker %d received %d\n", id, n)
	}
}

func createWorker(id int) chan<- int {
	c := make(chan int)
	go worker(id, c)
	return c
}

func test2() {
	var c1, c2 = generator(), generator() //Direct assignment when declaring
	var worker = createWorker(0)
	n := 0
	hasValue := false

	for {
		//Nil channels can run in select, although they cannot be selected to create a blocking
		//So here we take advantage of this to make a judgment
		var activeWorker chan<- int
		if hasValue {
			activeWorker = worker
		}
		select {
		case n = <-c1:
			hasValue = true
		case n = <-c2:
			hasValue = true
		case activeWorker <- n:
			hasValue = false

			//default:
			//If there is default here, it will fall into the dead cycle of default
			//The reason is that it takes a while for the two case s above to run before data is sent, so they get stuck in this dead loop
			//	fmt.Println("No value received")
		}
	}
}

func test3() {
	var c1, c2 = generator(), generator() //Direct assignment when declaring
	var worker = createWorker(0)
	//Because of the supply-demand relationship, c1,c2 provide faster data, while worker prints slower, resulting in many missing data in the middle
	//So here we have to cache it
	var values [] int
	//Timer, send a data after a specified time, return a channel, let's use case to fetch, hit and launch the program
	tm := time.After(time.Second * 10)
	//Timer, send message once per second, return a channel, let's use case to retrieve, hit hint how long the current queue is
	tick := time.Tick(time.Second * 1)
	for {
		var activeWorker chan<- int
		var activeValue int
		if len(values) > 0 {
			activeWorker = worker
			activeValue = values[0]
		}
		select {
		case n := <-c1:
			values = append(values, n)
		case n := <-c2:
			values = append(values, n)
		case activeWorker <- activeValue:
			values = values[1:]
		case <-time.After(800 * time.Millisecond):
			//Judging every time you loop, if you don't get data in 800 milliseconds, you'll be prompted
			fmt.Println("time out")
		case <-tick:
			//Count how long the current queue is per second
			fmt.Println("queue len=", len(values))
		case <-tm:
			//Restrict programs to run for only ten seconds
			fmt.Println("bye")
			return
		}
	}
}
func main() {
	//test1()
	//test2()
	test3()

}

Signal communication

This chapter is about how to use files, directories, network communication, and run other programs and external communications

IO Operation

The IO core of Go is interface io.Reader and io.Writer

Read and write from a file in go as long as the os package is used

package main
import "os"

func main(){
    //Create a 1024 byte memory space
    buf :=make([]byte, 1024)
    //Open File
    f, _ := os.Open("/etc/passwd")
    //close resource
    defer f.Close()
    //Dead cycle
    for {
        //1024 bytes read at a time
        n ,_ := f.Read(buf)
        //To the end of a large file
        if n ==0 {break}
        //Write content to os.Stdout for output printing
        os.Stdout.Write(buf[:n])
    }
}

If you want to use buffered IO, there are bufio packages

package main
import(
    "os"
    "bufio"    
)

func main(){
    buf := make([]byte, 1024)
    //Open File
    f, _ :=os.Open("/etc/passwd")
    defer f.Close()
    //Converting f to a buffered Reader,NewReader requires an io.Reader so you may think this will go wrong, but it won't.
    //Any Read() function implements this excuse, and *os.File has already done so
    r := bufio.NewReader(f)
    w := bufio.NewWriter(os.Stdout)
    defer w.Flush()
    for {
        //Read from Reader, write to Writer, and output files to screen
        n, _ := r.Read(buf)
        if n ==0 {break}
        w.Write(buf[0:n])
    }
}

io.Reader

As mentioned earlier, the io.Reader excuse is important for the Go language

Many functions require io.Reader to read some data as input

To satisfy this interface, only one method needs to be implemented: Read(p []byte)(n int,err error) writing is io.Writer, which implements the Write method

If you implement io.Reader or io.Writer interfaces for types in your programs or packages, the entire go standard library can use this type

A simple example

f, _ os.Open("/etc/passwd"); defer f.Close()
r := bufio.NewReader(f) //Make it a bufio to access the ReadString method
s,ok:=r.ReadString('\n') {/***/}   //Reads a line from the input, saves the string, and parses it using the string package

The similarities between the two examples show the scripting features that Go has.

Command Line Action

Parameters from the command line are obtained in the program through the string slice os.Args and imported into the package os.

flag packages have good interface consistency and provide a way to resolve identities.

An example is a DNS query tool:

//Define bool identity, dnssec variable must be a pointer, otherwise package cannot set its value
dnsser := flag.Bool("dnssec", false, "Request DNSSEC records")
//Similarly, the port option
port := flag.String("port","53","Set the query port")
//Simple redefinition of Usage functions
flag.Usage = func(){
    fmt.Fprintf(os.Stderr, "Usage : %s[OPTIONS][name ...]", os.Args[0])
    //For each identity specified, PrintDefaults will output help information
    flag.PrintDefaults()
}
//Resolve the identity and populate the variables
flag.Parse()

//When parameters are resolved, they can be used
if *dnssec{
    //do something
}

Execute Command

The os/exec package has functions to execute external commands and call system commands

Use by defining a *exec.Cmd structure with several methods

Execute ls-l:

cmd :=exec.Command("/bin/ls", "-l")
err := cmd.Run()

The example above runs ls-l but does not process the returned data, getting information from the standard output of the command line as follows:

import "exec"
cmd := exec.Command("/bin/ls","-l")
buf, err := cmd.Output()    //buf is a []byte

Network Operation

All network-related types and functions can be found in the net package, the most important of which is Dial

When Dial is on a remote system, this function returns the Conn interface type, which can be used to send or receive information

The Dial function simply abstracts the network and transport layers, so IPv4 or IPv6,TCP or UDP can share an interface

Connect to a remote system over TCP (port 80), then UDP, and finally TCP over IPV6, roughly the same

conn,e := Dial("tcp","172.16.8.7:80")
conn,e := Dial("udp","172.16.8.7:80")
conn,e := Dial("tcp", "[This is a IPV6 address]:80")    //Square brackets represent mandatory

If no error occurs, return by e and use conn to read from the socket

The original definition in package net is:

//Read data from a connection.
Read(b []byte)(n int,err error)

This makes conn io.Reader

//Write data to the connection.
Write(b []byte)(n int, err error)

This also makes conn io.Writer, which in fact is io.ReadWriter

But these are all implicit underlying layers, and you should always use higher-level packages, such as http packages

A simple htt Get as a case

package main
//Import Dependent Packages
import(
    "io/ioutil"
    "net/http"
    "fmt"
)

func main(){
    //Getting HTML pages using the Get method of http
    r,err := http.Get("http://www.baidu.com")
    //Simple error handling
    if err != nil {
        fmt.Printf("%s\n", err)//err.String() for use in books
        return
    }
    //Read adult content into b
    b,err := ioutil.ReadAll(r.Body)
    //You can also use this print to return results
    //Processing the return value, the second value identifies whether to print the body
	//byte, error := httputil.DumpResponse(response, true)
    //Close connection, //body resource requires close
    r.Body.Close()
    //Output Content
    if err ==nil{
        fmt.Printf("%s", string(b))
    }
}

http cases that can control the request header

package main

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

func simpleHttpDemo() {
	response, error := http.Get("http://www.baidu.com")
	//Error handling error
	if error != nil {
		panic(error)
	}
	//body Resource Requirements Must Be Closed
	defer response.Body.Close()

	//Processing the return value, the second value identifies whether to print the body
	byte, error := httputil.DumpResponse(response, true)
	if error != nil {
		panic(error)
	}
	fmt.Printf("%s\n", byte)
}

func httpControllerDemo() {
	//Control the content of the request
	request, err := http.NewRequest(http.MethodGet, "http://www.baidu.com", nil)
	if err != nil {
		panic(err)
	}
	//Set Request Header
	request.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36")
	//Send Request
	//Response, error: = http.DefaultClient.Do(request)//Default send request
	//Customize Request Content
	//You can set the transport proxy server CheckRedirect redirection to go here jar sets cookie timeout
	client := http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			fmt.Println("Redirect:", req)
			return nil
		},
	}
	response, error := client.Do(request)

	//Error handling error
	if error != nil {
		panic(error)
	}
	//body Resource Requirements Must Be Closed
	defer response.Body.Close()

	//Processing the return value, the second value identifies whether to print the body
	byte, error := httputil.DumpResponse(response, true)
	if error != nil {
		panic(error)
	}
	fmt.Printf("%s\n", byte)

}

func main() {
	//simpleHttpDemo()
	httpControllerDemo()
	//Send requests using http clients
	//Use http.Client to control the request header
	//Simplify your work with httputil

	//Performance analysis of http servers, you need an http server first
	//Import "net/http/pprof" The underlined identifier here, which I can use
	//Access/debug/pprof/
	//Using go tool pprof analysis to get CPU performance analysis within 30 seconds
	// For example, on the command line under the current web service, go tool pprof http://localhost:8888/debug/pprof/profile changes the profile to heap to see the memory usage, specifically the pprof or manual inside the goroot
	//After accessing this URL, a lot of things will be printed on the current command line, and the input web will be opened through the browser
}

Keywords: Go less JSON github

Added by Angry Coder on Tue, 23 Jul 2019 19:56:51 +0300