目录

Go基础

安装

https://go.dev/doc/install

然后设置代理

go env -w GO111MODULE=on
go env -w GOPROXY=https://proxy.golang.com.cn,direct

用户家目录会自动生成一个go文件夹,含有bin、src、pkg文件夹,其中src文件夹在GO111MODULE=“on"时没用,其他两个用于存放导入的外部包源码和二进制文件

When using modules, GOPATH is no longer used for resolving imports. However, it is still used to store downloaded source code (in GOPATH/pkg/mod) and compiled commands (in GOPATH/bin).

代码结构

code -> package -> module

包是在同一目录中的源文件集合,模块是一起发布的相关Go包的集合,存储库包含一个或多个模块(通常只有一个)

编写可执行的应用程序必须含有main包和main函数,编写可被导入的第三方包则不需要

包内的init函数最先运行

类型

推荐:https://tour.go-zh.org/list

bool
string
// int, uint 和 uintptr 在 32 位系统上通常为 32 位,在 64 位系统上则为 64 位
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名  表示一个 Unicode 码点
float32 float64
complex64 complex128

不同类型赋值均需显示转换

变量声明

var a int = 1
a := 2  //只能在函数内部使用
// 常量不能用 := 语法声明
const b = 3

语句

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

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

// 等价为c++中的while
for sum < 10 {
  sum += 1
}

// 无限循环
for {

}

if语句

if i < 1 {

}
// 可以在条件前加一个简单语句
if i := m; i < 10 {

}

switch:Go只运行选定的 case,而非之后所有的 case。实际上,Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。 除非以 fallthrough 语句结束,否则分支会自动终止。 Go 的另一点重要的不同在于 switch 的 case 无需为常量,且取值不必为整数。

switch os := runtime.GOOS; os {
  case "darwin":
    fmt.Println("OS X.")
  case "linux":
    fmt.Println("Linux.")
  default:
    fmt.Printf("%s.\n", os)
}
// 没有条件的 switch 同 switch true 一样,这种形式能将一长串 if-then-else 写得更加清晰
t := time.Now()
switch {
case t.Hour() < 12:
  fmt.Println("Good morning!")
case t.Hour() < 17:
  fmt.Println("Good afternoon.")
default:
  fmt.Println("Good evening.")
}

如果有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段 X。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以

结构体定义:

var a = Vertex{X:1, Y:2}
不需要如下形式 
var a Vertex = Vertex{X:1, Y:2}

方法

func关键字后括号中的为方法接收者类型,当方法接收者为指针类型时,方法可改变对象的值

var p *Vertex指针类型调用方法时,对值类型实现的Abs()方法可直接通过p.Abs()进行调用,编译器会自动转为(*p).Abs()

var v Vertex值类型调用方法时,对指针类型实现的Scale()方法可直接通过v.Scale()进行调用,编译器会自动转为(&v).Scale()

type Vertex struct {
  X, Y float64
}

// 为Vertex类型实现了Abs()方法
func (v Vertex) Abs() float64 {
  return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// 为*Vertex类型实现了Scale()方法
func (v *Vertex) Scale(f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}

接口

值类型与指针类型属于不同的类型

type Abser interface {
  Abs() float64
}

// 如下 Vertex类型实现了Abser接口,但*Vertex未实现,即*Vertex类型不能赋值给Abser,反之同理
func (v Vertex) Abs() float64 {
  return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// 空接口:指定了0个方法的接口
// 空接口可保存任何类型的值
var i interface{}

类型断言:提供了访问接口值的底层具体值的方式。

t, ok := i.(T)
 a, ok := i.(int64)

类型选择:

switch v := i.(type) {
  case int:
    fmt.Printf("Twice %v is %v\n", v, v*2)
  case string:
    fmt.Printf("%q is %v bytes long\n", v, len(v))
  default:
    fmt.Printf("I don't know about type %T!\n", v)
}

goroutine channel

go f(x, y, z) // 会启动一个新的 Go 协程并执行
f(x, y, z) // f, x, y 和 z 的求值发生在当前的 Go 协程中,而 f 的执行发生在新的 Go 协程中

ch := make(chan int, 100) // 信道在使用前必须创建
ch <- v    // 将 v 发送至 ch。
v := <-ch  // 从 ch 接收值并赋予 v。

发送者可通过 close 关闭一个channel来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试channel是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完v, ok := <-ch之后 ok 会被设置为 false。

循环for i := range ch会不断从信道接收值,直到它被关闭。只有发送者才能关闭信道,而接收者不能

select

select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行

当 select 中的其它分支都没有准备好时,default 分支就会执行

select {
  case c <- x:
    x, y = y, x+y
  case <-quit:
    fmt.Println("quit")
    return
  default:
    //
}