byte和rune

1
type byte = uint8

byte是 uint8 的别名,并且 byte始终与unit8相等,它用于区分字节值和8-bit的无符号整数值。

1
type rune = int32

rune是int32的别名,并且rune始终与int32相等,用于区分字符值和整数值。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
var a uint8
var b byte
fmt.Println(a == b) // true

var c rune
var d int32
fmt.Println(c == d) // true
}

append

1
func append(slice []Type, elems ...Type) []Type

将元素追加到切片末尾,超出最大容量会自动扩容,扩容规则:当前容量<1024,容量翻倍,翻倍后仍不足则新容量为翻倍后的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func main() {
// 1.内置函数将元素追加到切片末尾
a := make([]int, 3, 4)
fmt.Printf("value: %v, len: %d, cap: %d\n", a, len(a), cap(a)) // value: [0 0 0], len: 3, cap: 4
// 2.如果容量足够则追加到切片末尾
b := append(a, 1)
fmt.Printf("value: %v, len: %d, cap: %d\n", b, len(b), cap(b)) // value: [0 0 0 1], len: 4, cap: 4
// 3.如果容量不足时, 则重新分配一个新的基础数组, 追加元素并返回更新的切片
c := append(b, 2, 3, 4)
fmt.Printf("value: %v, len: %d, cap: %d\n", c, len(c), cap(c)) // value: [0 0 0 1 2 3 4], len: 7, cap: 8
// 4.通常会将append返回的结果保存到包含自身切片的变量中
c = append(c, 5)
fmt.Printf("value: %v, len: %d, cap: %d\n", c, len(c), cap(c)) // value: [0 0 0 1 2 3 4 5], len: 8, cap: 8

// 5.将字符串追加到字符切片末尾
d := []byte{'a', 'b', 'c'}
d = append(d, "cde"...)
fmt.Println(d) // [97 98 99 99 100 101]

// 6.将字符追加到字符串强制转化的字符切片末尾
e := append([]byte("hello"), "world"...)
fmt.Println(e) // [104 101 108 108 111 119 111 114 108 100]
}

copy

1
func copy(dst, src []Type) int

将元素从源切片复制到目的切片,特殊情况下它会将字节从字符串复制到字节数slice中,源和目标可能会重叠,返回复制的元素数量,该数量将是len(src)和len(dst)的最小值。

注意:

  • 目标slice需要初始化,否则会复制失败,返回0。
  • 复制到非空的目标数组会从头开始覆盖目标数组的元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
var a = []int{1, 2, 3, 4, 5}
var b = make([]int, 9)
num := copy(b, a)
fmt.Println(num) // 5
fmt.Println(b) // [1 2 3 4 5 0 0 0 0]

var c = []int{0, 0, 0, 0}
num1 := copy(c, a)
fmt.Println(num1) // 4
fmt.Println(c) // [1 2 3 4]
}

delete

1
func delete(m map[Type]Type1, key Type)

从map中删除指定键(map[key])的元素,如果map为nil或者不存在这样的元素,则不操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
var m map[string]int
fmt.Println(m) // nil
delete(m, "a")
fmt.Println(m) // nil

m = make(map[string]int)
m["a"] = 1
m["b"] = 2
fmt.Println(m) // map[a:1 b:2]

delete(m, "c")
fmt.Println(m) // map[a:1 b:2]

delete(m, "a")
fmt.Println(m) // map[b:2]
}

len

1
func len(v Type) int

返回数据类型值的长度:数组中的元素数量,数组指针中的元素数量,slice中的元素数量,map中的元素数量,字符串的字节数, channel(管道缓冲区排队的的元素数量)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

func main() {
// 数组
arr := [3]int{1, 2, 3}
fmt.Println(len(arr)) // 3
// 数组指针
fmt.Println(len(&arr)) // 3

// 切片
slice := []int{1, 2, 3, 4}
fmt.Println(len(slice)) // 4

// map
m := map[string]int{"a": 1, "b": 2}
fmt.Println(len(m)) // 2

// 字符串
fmt.Println(len("hello world")) // 11

// channel
ch := make(chan int, 5)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(len(ch)) // 3
}

cap

1
func cap(v Type) int

返回数据类型值的容量:数组(与len相同),数组指针(与len相同),slice,channel。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
// 数组
arr := [...]int{1, 2, 3, 4}
fmt.Println(cap(arr)) // 4
// 数组指针
fmt.Println(cap(&arr)) // 4

// 切片
slice := make([]int, 10, 20)
fmt.Println(cap(slice)) // 20

ch := make(chan int, 10)
ch <- 1
ch <- 2
fmt.Println(cap(ch)) // 10
}

close

1
func close(c chan<- Type)

close函数关闭通道,该通道必须为尚双向通道或者发送通道,而不能由接收者执行,并且具有在最后一个值发送之后关闭通道的作用,从关闭的通道中获取最后一个值之后,从通道中的任何接收都会成功并返回该通道内数据类型的零值。

对于关闭的通道:

1
2
close(c)
x, ok := <- c // ok将为false

panic

1
func panic(v interface{})

panic 函数终止当前goroutine的正常执行,当函数F调用 panic 时,F的正常执行立即停止,F的defer函数都会正常执行,然后F返回到调用方,对于调用方G,与F一样终止G的正常流程,并执行所有的defer函数,程序以非零的退出代码终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import "fmt"

func main() {
G()
}

func G() {
defer func() {
fmt.Println("exec G defer_A")
}()
defer func() {
fmt.Println("exec G defer_B")
}()

F()

fmt.Println("G end")
}

func F() {
defer func() {
fmt.Println("exec F defer_A")
}()
defer func() {
fmt.Println("exec F defer_B")
}()

panic("出现异常")

fmt.Println("F end")
}

输出结果:

1
2
3
4
5
exec F defer_B
exec F defer_A
exec G defer_B
exec G defer_A
panic: 出现异常

recover

1
func recover() interface{}

recover是用于从panic恢复的函数,recover函数只有在defer的函数里才能发挥真正的作用,如果正常情况下(没有发生panic)调用recover会返回nil并且不会有任何影响,如果当前goroutine发生panic,recover的调用将会捕获到panic的值,并且恢复正常执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
fmt.Println("Main start")
G()
fmt.Println("Main end")
}

func G() {
defer func() {
if err := recover(); err != nil {
fmt.Printf("捕获错误: %s\n", err)
}
}()
fmt.Println("G start")
panic("G exec error")
fmt.Println("G end")
}

输出结果

1
2
3
4
Main start
G start
捕获错误: G exec error
Main end

recover使用中的坑

在捕获了panic的函数中又启动其他协程,其他协程中发生的panic在当前函数中是不能捕获到的,直接就结束程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
"sync"
)
var wg sync.WaitGroup

func main() {
wg.Add(1)
fmt.Println("Main start")
G()
fmt.Println("Main end")
wg.Wait()
}

func G() {
defer func() {
if err := recover(); err != nil {
fmt.Printf("捕获错误: %s\n", err)
}
}()
fmt.Println("G start")
go F()
fmt.Println("G end")
}

func F() {
defer wg.Done()
fmt.Println("F start")
panic("G exec error")
fmt.Println("F end")
}

输出结果:

1
2
3
4
5
6
Main start
G start
G end
Main end
F start
panic: G exec error

new 和 make 关键字的区别和使用场景

newmake 在Go标准包 builtin 下。(builtin/builtin.go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
// Slice: The size specifies the length. The capacity of the slice is
// equal to its length. A second integer argument may be provided to
// specify a different capacity; it must be no smaller than the
// length. For example, make([]int, 0, 10) allocates an underlying array
// of size 10 and returns a slice of length 0 and capacity 10 that is
// backed by this underlying array.
// Map: An empty map is allocated with enough space to hold the
// specified number of elements. The size may be omitted, in which case
// a small starting size is allocated.
// Channel: The channel's buffer is initialized with the specified
// buffer capacity. If zero, or the size is omitted, the channel is
// unbuffered.
func make(t Type, size ...IntegerType) Type

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

new 内置函数分配内存。第一个参数是类型,而不是值,返回的值是指向该类型新分配的零值的指针。

new 不常用:new 在一些需要实例化接口的地方用的较多,但是可以用 &A{} 代替。

但是 new 和 &A{} 也是有差别的,主要差别在于 &A{} 显示执行堆分配。

make 也是用于内存分配的,但是和 new 不同,它只用于 slicemap 以及 channel 的内存创建,而且它返回的类型就是这三个类型本身,而不是它们的指针类型,因为这三种类型就是引用类型,所以就没必要返回它们的指针了。

make

  • slice: make(type, len, cap) 初始化 slice时,第一个参数指定切片类型,提供第二个参数指定slice的长度,第三个参数(可选)用来指定slice容量,len 必须<= cap。
  • map:为map分配足够的空间大小容纳指定数量的元素,初始大小较小。
  • chan:可以指定channel缓冲区的大小(异步channel),也可以不指定缓冲区大小,则channel无缓冲区(同步channel)

这三种类型是引用类型,所以必须初始化,但是不是置为零值,这个跟 new 是不一样的。

举例说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
a := new([]int)
fmt.Println(a) // &[]
fmt.Println(a != nil) // true , 是该类型的零值,并不是nil
(*a)[0] = 18 //panic: runtime error: index out of range [0] with length 0

b := make([]int, 10, 20)
fmt.Println(b) // [0 0 0 0 0 0 0 0 0 0]
b[1] = 18 // ok
}

make + append注意事项:

当使用make([]int, 10, 20)初始化一个 slice 时,会初始化 len 个零值,使用 append 时会从初始化的零值往后添加元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
arr := make([]int, 10, 10)
fmt.Println(arr, &arr[0]) // [0 0 0 0 0 0 0 0 0 0] 0xc0000200a0

arr[0] = 1
fmt.Println(arr, &arr[0]) // [1 0 0 0 0 0 0 0 0 0] 0xc0000200a0

arr = append(arr, 1)
fmt.Println(arr, &arr[0]) // [1 0 0 0 0 0 0 0 0 0 1] 0xc000104000
}

逃逸分析