环境搭建

VSCode 下载地址: https://code.visualstudio.com/

下载慢解决方法:

  • 官网找到需要的版本后,点击下载,然后复制下载链接
  • 将红框内地址更改为国内镜像地址 vscode.cdn.azure.cn

img

  • 浏览器复制链接后重新下载

Golang 下载地址: https://golang.google.cn/dl/

  1. VSCode 安装 Go 插件

  2. 更新 Go 工具

    在更新之前需要说明的是,由于国内政策,没办法直接更新,需要代理

    1# 旧版,已废弃
    2go env -w GO111MODULE=on
    3go env -w GOPROXY=https://goproxy.io,direct
    
    1# 新版改成如下链接
    2go env -w GO111MODULE=on
    3go env -w GOPROXY=https://proxy.golang.com.cn,direct
    

    我这里报错warning: go env -w GOPROXY=... does not override conflicting OS environment variable原因是已经有一个了,查看了环境变量是PC前主人的,手动更改了环境变量,而后关掉并重新打开 VSCode,继续下面的步骤。

    • 在Visual Studio Code中,打开命令面板的**“帮助**>显示所有命令”。 或者使用键盘快捷方式 (Ctrl+Shift+P)
    • Go: Install/Update tools搜索 ,然后从托盘运行命令
    • 出现提示时,选择所有可用的 Go 工具,然后单击“确定”。
    • 等待 Go 工具完成更新。

    image-20230704104923435

    image-20230704104933532

time

time.sleep

golang 的睡眠函数time.sleep(1)time.sleep(1 * time.Second)

前者仅睡眠$1$纳秒,后者才是$1$秒

time.Now().Format(“2006-01-02 15:04:05”)

必须是这个时间点,才能格式化。


TrimRight(TrimLeft)

函数:strings.TrimRight(s string, cutset string) 定义:把cutset里面的字符串拆分成字符,然后从右往左,逐个比对字符串中的每个字符,直到遇到没有在cutset中出现的字符。

 1package main
 2
 3import (
 4	"fmt"
 5	"strings"
 6)
 7
 8func main() {
 9	fmt.Println("aabbccdd\t:", strings.TrimLeft("aabbccdd", "abcd"))  // 空字符串
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"))  // 空字符串
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

iota

iotaGolang语言的常量计数器,只能在常量的const表达式中使用,在const关键字出现的时将被重置为0const中每新增一行常量声明iota自增'1’iota可以理解为const语句块中的行索引)。

iota只能在常量的表达式中使用,iotaconst关键字出现时将被重置为0,不同const定义块互不干扰。

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)

fallthrough

Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的**case**代码。

append()

  • append()用来将元素添加到切片末尾并返回结果。
  • 调用append()函数必须用原来的切片变量接收返回值
  • append()追加元素,如果slice还有容量的话,就会将新的元素放在原来slice后面的剩余空间里,当底层数组装不下的时候,Go就会创建新的底层数组来保存这个切片,slice地址也随之改变。
  • 分配了新的地址后,再把原来slice中的元素逐个拷贝到新的slice中,并返回。
 1package  main
 2import "fmt"
 3//切片进阶操作
 4 
 5func main(){
 6	//append()为切片追加元素
 7	s1 := []string {"火鸡面","辛拉面","汤达人"}
 8	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n",s1,len(s1),cap(s1))
 9	
10	//调用append函数必须用原来的切片变量接收返回值
11	s1 = append(s1,"小当家") //append动态追加元素,原来的底层数组容纳不下足够多的元素时,切片就会开始扩容,Go底层数组就会把底层数组换一个
12	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n",s1,len(s1),cap(s1))
13
14	//调用append添加一个切片
15	s2 := []string{"脆司令","圣斗士"}
16	s1 = append(s1,s2...)//...表示拆开切片,再添加
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*/

string 和 byte 的转换

1s1 := "hello"
2b := []byte(s1) // string to []byte
3s2 := string(b) // []byte to string

Interface 用法

匿名字段和内嵌结构体

当我们需要重写一个 “实现了某个接口的结构体” 的部分方法,而其它方法保持不变 的时候,就需要用到这种用法。

 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 实现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// 构造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}

引入其他文件的struct

struct的属性是否被导出,也遵循大小写的原则:首字母大写的被导出,首字母小写的不被导出。

internal 包

protobuf

1type NATSession struct {
2    Vrf   int32    `protobuf:"varint,1,opt,name=vrf,proto3" json:"vrf,omitempty"`
3}

在 Golang 中,结构体中的int32后面的protobuf标记是用于指定结构体字段在序列化和反序列化时的编码格式。这是由于该结构体可能用于Protocol Buffers(protobuf)的序列化和反序列化操作。

omitempty

  1. 没有 json 标记时默认字段名称大写则序列化时默认使用该字段名。
  2. 没有 json 标记时默认字段名称小写则序列化不会包含在内。
  3. 有 json 标记时没有 omitempty 标记,序列化时将使用配置的 json 名称(字段大写时)
  4. 有 json 标记时有 omitempty 标记,序列化时将忽略有 omitempty 并且没有赋值的字段,当具有值时则显示。
  5. 有 json 标记时但名称为-时,当该字段值为空,则序列化时将忽略。

当 struct 中的字段没有值时, json.Marshal() 序列化的时候不会忽略这些字段,而是默认输出字段的类型零值(例如intfloat类型零值是 0,string类型零值是"",对象类型零值是 nil)。如果想要在序列序列化时忽略这些没有值的字段时,可以在对应字段添加omitempty tag。

 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// 在tag中添加omitempty忽略空值
 2// 注意这里 hobby,omitempty 合起来是json tag值,中间用英文逗号分隔
 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":"左右逢源"} // 序列化结果中没有email和hobby字段
11*/

简式的变量声明

仅可以在函数内部使用

1package main
2myvar := 1 //error
3func main() {  
4}

重复声明变量

不能在一个单独的声明中重复声明一个变量,但在多变量声明中这是允许的,其中至少要有一个新的声明变量。 重复变量需要在相同的代码块内,否则将得到一个隐藏变量。

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}

命名返回值

Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。

没有参数的 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*/

for 循环

Go 只有一种循环结构:for 循环。

基本的 for 循环由三部分组成,它们用分号隔开:

  • 初始化语句:在第一次迭代前执行
  • 条件表达式:在每次迭代前求值
  • 后置语句:在每次迭代的结尾执行

初始化语句和后置语句是可选的。

1func main() {
2	sum := 1
3	for ; sum < 1000; {
4		sum += sum
5	}
6	fmt.Println(sum) // 1024
7}

去掉分号,可以理解为 while 循环。

1func main() {
2	sum := 1
3	for sum < 1000 {
4		sum += sum
5	}
6	fmt.Println(sum) // 1024
7}

if 语句

if 语句可以在条件表达式前执行一个简单的语句,该语句声明的变量作用域仅在 if 之内。

if 的简短语句中声明的变量同样可以在任何对应的 else 块中使用。

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 了
8	return lim
9}

switch 语句

没有条件的 switch 同 switch true 一样,此时相当于是if-then-else语句。

defer 语句

defer 语句会将函数推迟到外层函数返回之后执行。

推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。

 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*/