Go快速笔记

1. := 与var定义

在函数中,:= 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。

函数外的每个语句都必须以关键字开始(varfunc、等等),:= 结构不能使用在函数外。

常量用const定义

slice channle map 必须使用make函数来定义

new 分配内存后置零,返回指针

make 分配内存后初始化, 返回对象

在满足下列条件时,已被声明的变量 v 可出现在:= 声明中:

  1. 本次声明与已声明的 v 处于同一作用域中,(若 v 已在外层作用域中声明过,则此次声明会创建一个新的变量v)
  2. 在初始化中与其类型相应的值才能赋予 v,且在此次声明中至少另有一个变量是新声明的。

2. 基本数据类型

bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名
// 代表一个Unicode码
float32 float64
complex64 complex128
单引号字符常量表示 Unicode Code Point,⽀支持 \uFFFF、\U7FFFFFFF、\xFF 格式。 对应 rune 类型,UCS-4。
var c1, c2 rune = '\u6211', '们'

3.Channel

channel 操作符 <- ,操作符只有这一个,这样就不会搞错位置了

发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过

v, ok := <-ch
之后 ok 会被设置为 false

循环 for i := range c 会不断从 channel 接收值,直到它被关闭。

注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。

还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 range

4.select

select 语句使得一个 goroutine 在多个通讯操作上等待。

select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。

select 中的其他条件分支都没有准备好的时候,default 分支会被执行。

为了非阻塞的发送或者接收,可使用 default 分支:

select {
case i := <-c:
// 使用 i
default:
// 从 c 读取会阻塞
}

5.for range 遍历 range 会复制对象

for key, value := range oldMap {
newMap[key] = value
}

//若你只需要该遍历中的第一个项(键或下标),去掉第二个就行了:

for key := range m {
if key.expired() {
delete(m, key)
}
}

6.Switch

switch 可以什么都不写 当多级if else使用 还可以当做判断类型使用 t.(type) 只能在switch使用

switch t := t.(type)

7.Map

key不存在不会报错,会返回与map的value的类型对应的零值. 用if _,ok:=someMap[key];ok {} 来判断有没有值

8.Print

  1. 字符串函数(Sprintf 等)会返回一个字符串,而非填充给定的缓冲区。
  2. fmt.Fprint 一类的格式化打印函数可接受任何实现了io.Writer接口的对象作为第一个实参:如os.Stdout`os.Stderr`
  3. fmt.Printf
    • %v(对应“值”)map中的键可能按任意顺序输出。
    • 当打印结构体时,改进的格式 %+v 会为结构体的每个字段添上字段名,而另一种格式 %#v 将完全按照Go的语法打印值。
    • 当遇到 string 或 []byte 值时, 可使用 %q 产生带引号的字符串;而格式 %#q 会尽可能使用反引号。
    • %T 类型
    • 若你想控制自定义类型的默认格式,只需为该类型定义一个具有 String() string 签名的方法。

9.append 中extend

x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)
fmt.Println(x)

10.类型转换

要提取我们知道在该值中的字符串,可以这样:

str := value.(string)
但若它所转换的值中不包含字符串,该程序就会以运行时错误崩溃。为避免这种情况, 需使用“逗号, ok”惯用测试它能安全地判断该值是否为字符串:

str, ok := value.(string) 
if ok {
fmt.Printf("字符串值为 %q\n", str)
} else {
fmt.Printf("该值非字符串\n")
}

若类型断言失败,str 将继续存在且为字符串类型,但它将拥有零值,即空字符串。

11. x:=x

req := req
但在Go中这样做是合法且惯用的。你用相同的名字获得了该变量的一个新的版本, 以此来局部地刻意屏蔽循环变量,使它对每个Go程保持唯一。
比如gorm中

12.函数

变参 变参本质上就是 slice。只能有⼀一个,且必须是最后⼀一个。

func test(s string, n ...int) string {
var x int
for _, i := range n {
x += i
}
return fmt.Sprintf(s, x)
}
func main() {
println(test("sum: %d", 1, 2, 3))
}

使⽤用 slice 对象做变参时,必须展开。

func main() { 
s := []int{1, 2, 3
}
println(test("sum: %d", s...)) }

匿名函数可赋值给变量,做为结构字段,或者在 channel ⾥里传送

13.Defer

func add(x, y int) (z int) {
defer func() {
println(z) // 输出: 203
}()
z = x + y return z + 200
// 执⾏行顺序: (z = z + 200) -> (call defer) -> (ret)
}

多个 defer 注册,按 FILO 次序执⾏行。哪怕函数或某个延迟调⽤用发⽣生错误,这些调⽤用依旧 会被执⾏行。

func test(x int) {
defer println("a") defer println("b")
defer func() { println(100 / x) }()
defer println("c")
// div0 异常未被捕获,逐步往外传递,最终终⽌止进程。
}
func main() { test(0) }

输出:
c b a panic: runtime error: integer divide by zero

14. error

捕获函数 recover 只有在延迟调⽤用内直接调⽤用才会终⽌止错误,否则总是返回 nil。任何未捕获的错误都会沿调⽤用堆栈向外传递。

导致关键流程出现不可修复性错误的 使⽤用 panic,其他使⽤用 error。

15. 数组

a := [3]int{1, 2}// 未初始化元素值为 0。 
b := [...]int{1, 2, 3, 4}// 通过初始化值确定数组⻓长度。
c := [5]int{2: 100, 4:200} // 使⽤用索引号初始化元素。
d := [...]struct { name string age uint8 }{ {"user1", 10}, {"user2", 20}, } // 可省略元素类型。 // 别忘了最后⼀一⾏行的逗号。
⽀支持多维数组。
a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能⽤用 "..."。

16. slice

arrayslice是不同的类型,不同长度的array类型不同 slice是[]int array是[n]int

官方文档中说

In Go, array is a fixed length of continuous memory with specified type, while slice is just a reference which points to an underlying array. Since they are different types, they can't assign value each other directly.