1. Environment Setup
VSCode Download Link: https://code.visualstudio.com/
Solutions for Slow Downloads:
- Find the required version on the official website, click download, and then copy the download link.
- Replace the address in the red box with the domestic mirror address
vscode.cdn.azure.cn
.

- Paste the link in the browser and download again.
Golang Download Link: https://golang.google.cn/dl/
Install the Go plugin in VSCode.
Update Go tools.
Before updating, it’s important to note that due to domestic policies, direct updates are not possible, and a proxy is required.
1# Old version, deprecated 2go env -w GO111MODULE=on 3go env -w GOPROXY=https://goproxy.io,direct
1# New version, use the following link 2go env -w GO111MODULE=on 3go env -w GOPROXY=https://proxy.golang.com.cn,direct
I encountered an error
warning: go env -w GOPROXY=... does not override conflicting OS environment variable
because there was already one set. After checking the environment variables, I found that they were set by the previous owner of the PC. I manually changed the environment variables, closed and reopened VSCode, and continued with the steps below.- In Visual Studio Code, open the Command Palette by going to Help > Show All Commands. Alternatively, use the keyboard shortcut (Ctrl+Shift+P).
- Search for
Go: Install/Update tools
, then run the command from the tray. - When prompted, select all available Go tools and click “OK”.
- Wait for the Go tools to finish updating.
2. Basic Syntax
2.1 time
2.1.1 time.sleep
The sleep function in golang
time.sleep(1)
and time.sleep(1 * time.Second)
.
The former only sleeps for 1 nanosecond, while the latter sleeps for 1 second.
2.1.2 Format
1time.Now().Format("2006-01-02 15:04:05") // Must use this specific time point to format
2.2 TrimRight(TrimLeft)
Function: strings.TrimRight(s string, cutset string)
Definition: Splits the string cutset
into characters, then compares each character in the string from right to left until it encounters a character not present in cutset
.
1package main
2
3import (
4 "fmt"
5 "strings"
6)
7
8func main() {
9 fmt.Println("aabbccdd\t:", strings.TrimLeft("aabbccdd", "abcd")) // Empty string
10 fmt.Println("aabbccdde\t:", strings.TrimLeft("aabbccdde", "abcd")) // e
11 fmt.Println("aabbeccdd\t:", strings.TrimLeft("aabbedcba", "abcd")) // edcba
12 fmt.Println("aabbccdd\t:", strings.TrimRight("aabbccdd", "abcd")) // Empty string
13 fmt.Println("aabbccdde\t:", strings.TrimRight("aabbccdde", "abcd")) // aabbccdde
14 fmt.Println("aabbeccdd\t:", strings.TrimRight("aabbedcba", "abcd")) //aabbe
15}
16
17**********************************
18aabbccdd :
19aabbccdde : e
20aabbeccdd : edcba
21aabbccdd :
22aabbccdde : aabbccdde
23aabbeccdd : aabbe
2.3 iota
iota
is a constant counter in Golang, which can only be used in constant expressions. It is reset to 0
when the const
keyword appears, and increments by 1
for each new constant declaration in const
(you can think of iota
as the line index in the const
block).
iota
can only be used in constant expressions. It is reset to 0
when the const
keyword appears, and different const
blocks do not interfere with each other.
1const a = iota // a = 0 => iota = 0, a = iota
2const (
3 b = iota // b = 0 => iota = 0, b = iota
4 c // c = 1 => iota ++, c = iota
5 d // d = 2 => iota ++, d = iota
6)
2.4 fallthrough
In Go, switch
is equivalent to having a break
at the end of each case
by default. After a successful match, it does not automatically execute the following case
but exits the entire switch
. However, you can use fallthrough
to force the execution of the subsequent case
code.
2.5 append
append()
is used to add elements to the end of a slice and return the result.- The return value of
append()
must be received by the original slice variable. - When appending elements, if the
slice
still has capacity, the new elements will be placed in the remaining space behind the originalslice
. When the underlying array cannot accommodate more elements, Go will create a new underlying array to hold the slice, and theslice
address will also change. - After allocating a new address, it will copy the elements from the original
slice
to the newslice
one by one and return it.
1package main
2import "fmt"
3// Advanced slice operations
4
5func main(){
6 // append() adds elements to a slice
7 s1 := []string {"火鸡面","辛拉面","汤达人"}
8 fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n",s1,len(s1),cap(s1))
9
10 // The return value of append must be received by the original slice variable
11 s1 = append(s1,"小当家") // append dynamically adds elements. When the original underlying array cannot accommodate enough elements, the slice will start to expand, and Go will replace the underlying array.
12 fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n",s1,len(s1),cap(s1))
13
14 // Call append to add a slice
15 s2 := []string{"脆司令","圣斗士"}
16 s1 = append(s1,s2...) // ... means to unpack the slice and then add it
17 fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d",s1,len(s1),cap(s1))
18}
19
20/*
21 s1=[火鸡面 辛拉面 汤达人] len(s1)=3 cap(s1)=3
22 s1=[火鸡面 辛拉面 汤达人 小当家] len(s1)=4 cap(s1)=6
23 s1=[火鸡面 辛拉面 汤达人 小当家 脆司令 圣斗士] len(s1)=6 cap(s1)=6
24*/
2.6 string and byte conversion
1s1 := "hello"
2b := []byte(s1) // string to []byte
3s2 := string(b) // []byte to string
2.7 Interface usage
2.7.1 Anonymous fields and embedded structs
When we need to override some methods of a “struct that implements an interface” while keeping other methods unchanged, we need to use this approach.
1package main
2
3import (
4 "fmt"
5)
6
7type Interface interface {
8 Less(i, j int) bool
9 Swap(i, j int)
10}
11
12// Array implements the Interface interface
13type Array []int
14
15func (arr Array) Less(i, j int) bool {
16 return arr[i] < arr[j]
17}
18
19func (arr Array) Swap(i, j int) {
20 arr[i], arr[j] = arr[j], arr[i]
21}
22
23// Anonymous interface
24type reverse struct {
25 Interface
26}
27
28// Override
29func (r reverse) Less(i, j int) bool {
30 return r.Interface.Less(j, i)
31}
32
33// Construct reverse Interface
34func Reverse(data Interface) Interface {
35 return &reverse{data}
36}
37
38func main() {
39 arr := Array{1, 2, 3}
40 rarr := Reverse(arr)
41 fmt.Println(arr.Less(0,1))
42 fmt.Println(rarr.Less(0,1))
43}
2.8 Importing structs from other files
Whether a struct
’s properties are exported also follows the capitalization rule: those with uppercase first letters are exported, while those with lowercase first letters are not exported.
2.9 internal package
I didn’t pay attention to the special meaning of this package name before. I only knew it was internal, but I didn’t realize that Go directly sets packages with this name to have module-level private access. Previously, .pb.go
files generated by proto
were placed in the internal
package in my project. I tried to import them in other projects but kept failing with the error use of internal package xxx not allowed.
After researching, I found that in Go 1.5 and later versions, you can create an internal
package to make some program entities only accessible to other code within the current module. This is the third type of access: module-level private. Go packages in the internal
directory of a Go project can only be imported by packages within the project. External projects cannot import packages from this internal
directory.
1.
2├── module1
3│ ├── go.mod
4│ ├── internal
5│ │ └── pkga
6│ ├── pkg1
7│ └── pkg2
8└── module2
9 ├── go.mod
10 └── pkg1
The internal/pkga
package in module1
can be imported by pkg1
and pkg2
in module1
. However, it cannot be imported by pkg1
in module2
.
2.10 protobuf
1type NATSession struct {
2 Vrf int32 `protobuf:"varint,1,opt,name=vrf,proto3" json:"vrf,omitempty"`
3}
In Golang, the protobuf
tag after int32
in a struct is used to specify the encoding format of the struct field during serialization and deserialization. This is because the struct is used for Protocol Buffers (protobuf) serialization and deserialization operations.
2.11 omitempty
- When there is no
json
tag, the default field name is capitalized, and the default field name is used during serialization. - When there is no
json
tag, the default field name is lowercase, and it will not be included in the serialization. - When there is a
json
tag but noomitempty
tag, the configuredjson
name will be used during serialization (when the field is capitalized). - When there is a
json
tag with anomitempty
tag, fields withomitempty
that are not assigned a value will be ignored during serialization. When a value is assigned, it will be displayed. - When there is a
json
tag but the name is-
, the field will be ignored during serialization if it is empty.
When a field in a struct
has no value, json.Marshal()
will not ignore these fields during serialization but will output the type’s zero value by default (e.g., int
and float
types default to 0, string
type defaults to ""
, and object types default to nil). If you want to ignore fields without values during serialization, you can add the omitempty
tag to the corresponding field.
1type User struct {
2 Name string `json:"name"`
3 Email string `json:"email"`
4 Hobby []string `json:"hobby"`
5}
6
7func omitemptyDemo() {
8 u1 := User{
9 Name: "左右逢源",
10 }
11 // struct -> json string
12 b, err := json.Marshal(u1)
13 if err != nil {
14 fmt.Printf("json.Marshal failed, err:%v\n", err)
15 return
16 }
17 fmt.Printf("str:%s\n", b)
18}
19
20/*
21 str:{"name":"左右逢源","email":"","hobby":null}
22*/
1// Add omitempty in the tag to ignore empty values
2// Note that hobby,omitempty together is the json tag value, separated by an English comma
3type User struct {
4 Name string `json:"name"`
5 Email string `json:"email,omitempty"`
6 Hobby []string `json:"hobby,omitempty"`
7}
8
9/*
10 str:{"name":"左右逢源"} // The serialized result does not include the email and hobby fields
11*/
2.12 Short variable declarations
2.12.1 Can only be used inside functions
1package main
2myvar := 1 //error
3func main() {
4}
2.12.2 Redeclaring variables
You cannot redeclare a variable in a single declaration, but it is allowed in multi-variable declarations, where at least one new variable must be declared. Repeated variables need to be within the same code block; otherwise, you will get a hidden variable.
1package main
2func main() {
3 one := 0
4 one := 1 //error
5}
1package main
2func main() {
3 one := 0
4 one, two := 1,2
5 one,two = two,one
6}
2.13 Named return values
Go’s return values can be named, and they are treated as variables defined at the top of the function.
A return
statement without arguments returns the named return values. This is known as a naked
return.
1package main
2
3import "fmt"
4
5func split(sum int) (x, y int) {
6 x = sum / 3
7 y = sum - x
8 return
9}
10
11func main() {
12 fmt.Println(split(9))
13}
14
15/*
16 3 6
17*/
2.14 for loop
Go has only one loop structure: the for
loop.
The basic for
loop consists of three parts separated by semicolons:
- Initialization statement: Executed before the first iteration.
- Condition expression: Evaluated before each iteration.
- Post statement: Executed at the end of each iteration.
The initialization and post statements are optional.
1func main() {
2 sum := 1
3 for ; sum < 1000; {
4 sum += sum
5 }
6 fmt.Println(sum) // 1024
7}
Removing the semicolons makes it similar to a while loop.
1func main() {
2 sum := 1
3 for sum < 1000 {
4 sum += sum
5 }
6 fmt.Println(sum) // 1024
7}
2.15 if statement
The if
statement can execute a simple statement before the conditional expression, and the variable declared in this statement is only scoped within the if
.
Variables declared in the short statement of if
can also be used in any corresponding else
blocks.
1func pow(x, n, lim float64) float64 {
2 if v := math.Pow(x, n); v < lim {
3 return v
4 } else {
5 fmt.Printf("%g >= %g\n", v, lim)
6 }
7 // v cannot be used here
8 return lim
9}
2.16 switch statement
A switch
without a condition is the same as switch true
, which is equivalent to an if-then-else
statement.
2.17 defer statement
The defer
statement postpones the execution of a function until the surrounding function returns.
Deferred function calls are pushed onto a stack. When the surrounding function returns, the deferred functions are called in last-in-first-out order.
1func main() {
2 fmt.Println("counting")
3
4 for i := 0; i < 3; i++ {
5 defer fmt.Println(i)
6 fmt.Println(i)
7 }
8
9 fmt.Println("done")
10}
11/*
12 counting
13 0
14 1
15 2
16 done
17 2
18 1
19 0
20*/