[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.
- go get command demonstration
- Use gopm to get packages that cannot be downloaded
- go build compiles the go file we write, but it will be built in the current directory
- 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
- 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)
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
- new assignment; make initialization
- The top two ends can be summarized as
- new(T) returns *T pointing to a zero value T
- make(T) returns the initialized T
- make, of course, only works with slice,map, and channel
Constructors and compound declarations
- 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 }
- 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 }
- 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}
- 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)
- 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
- Create a function to receive parameters of this type
func doSomething(in1 *NameAge, in2 int){/*...*/}
- 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
- 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
- Then convert
var b bar = bar{1} //Declare B as bar type var f foo = b //Assign B to F
- The last line causes an error
connot use b(type bar) as type foo in assignment
- 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.
- 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}
- 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)
- 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) }
- 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.
- 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}
- 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:
- 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 }
- Define a new type for sorting slices, note that the slice type is defined:
type Xi []int type Xs []string
- 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]}
- 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:
- 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().
- 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 }