ZYB ARTICLES REPOS

关于go语言中的一些陷阱总结

for和switch语句中defer的执行时机

先看一下测试代码

package main
import (
    "fmt"
    "time"
)

func main() {
    defer fmt.Println("Program Exited...")

    for n:=0; n<5; n++ {
        fmt.Printf("ROUND: %v\n", n)
        defer fmt.Printf("for defer executed with n==%v\n", n)              // for-defer
        switch n % 2 {
        case 0:
            defer fmt.Printf("switch defer executed with n==%v\n", n)       // switch-defer
            fmt.Println("case 0")
        case 1:
            fmt.Println("case 1")
        default:
            fmt.Println("default")
        }

        time.Sleep(1*time.Second)
    }

    fmt.Println("before program exited")
}

初看这个代码会觉得

但实际运行结果如下

ROUND: 0
case 0
ROUND: 1
case 1
ROUND: 2
case 0
ROUND: 3
case 1
ROUND: 4
case 0
before program exited
switch defer executed with n==4
for defer executed with n==4
for defer executed with n==3
switch defer executed with n==2
for defer executed with n==2
for defer executed with n==1
switch defer executed with n==0
for defer executed with n==0
Program Exited...

可以看到switch-deferfor-defer都是在main函数返回前执行的,因此可以得出结论:forswitch的作用域内的defer的级别和forswitch是相等价的

slice的插入

如果想在[]int的slice里某个位置插入一个元素,一般是在这个位置把它分成两个slice,然后把要插入的元素append到前一个slice之后,再append后一个slice。

但是这里slice操作容易出错,如下代码:

package main

import (
	"fmt"
)

func main() {
	data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}

	// 要插入的元素
	x := 9

	// 要插入的位置
	index := 3

	// 先保留slice的后一部分
	rear := data[index:]

	// 把元素插入slice的前一部分
	data = append(data[:index], x)

	// 将尾部slice组合进来
	data = append(data, rear...)
	fmt.Printf("%v\n", data)
}

可以看到最终的结果并不是我们预期的:

[0 1 2 9 3 4 5 6 7 8]

而是

[0 1 2 9 9 4 5 6 7 8]

这里的问题出在rear := data[index:],这时rear的数据段只是data数据段的一部分,因此data如果有修改,势必会影响到rear的值。

为了解决这个问题,rear必需重新创建。改成如下方式即可:

rear := append([]int{}, data[index:]...)

for-闭包-协程的问题

func main() {
	for i := 0; i < 3; i++ {
		go func() {
			fmt.Printf("%v\n", i)
		}()
	}

	time.Sleep(1 * time.Second)
}

其输出是

3
3
3

并不是我们预期的012

主要是每次协程都用了同一个变量i,而每次循环这个值都会被统一改变。

修改方法

func main() {
	for i := 0; i < 3; i++ {
		go func(x int) {
			fmt.Printf("%v\n", x)
		}(i)
	}

	time.Sleep(1 * time.Second)
}

输出

2
0
1

顺序不固定

for-switch-break不会跳出for循环问题

package main

import (
	"fmt"
	"time"
)

func main() {
	defer fmt.Println("Program Exited...")

	cnt := 0

	for {
		fmt.Printf("cnt %v\n", cnt)

		switch cnt {
		case 3:
			break
		}
		
		cnt++
		time.Sleep(1)
	}

}

这个是死循环

copy slice的坑

copy的目标slice必需是已经分配了长度的。要不然是copy不进去的。

func testCopyToSlice() {
	src := []byte("test")
	dst1 := make([]byte, 4)
	dst2 := make([]byte, 0, 4)

	copy(dst1, src)
	copy(dst2, src)
	fmt.Printf("dst1 %v\n", string(dst1))
	fmt.Printf("dst2 %v\n", string(dst2))
}

输出

dst1 test
dst2 

bytes.Buffer

bytes.Buffer的空间是会自动增长的,无论初始给的长度是多少。

func testBuffer() {
	b := bytes.NewBuffer(make([]byte, 0, 2))
	for i := 1; i <= 16; i++ {
		b.Write([]byte{byte(i)})
		fmt.Printf("cap %2d: bytes: %v\n", b.Cap(), b.Bytes())
	}
}

输出

cap  2: bytes: [1]
cap  2: bytes: [1 2]
cap  5: bytes: [1 2 3]
cap  5: bytes: [1 2 3 4]
cap  5: bytes: [1 2 3 4 5]
cap 11: bytes: [1 2 3 4 5 6]
cap 11: bytes: [1 2 3 4 5 6 7]
cap 11: bytes: [1 2 3 4 5 6 7 8]
cap 11: bytes: [1 2 3 4 5 6 7 8 9]
cap 11: bytes: [1 2 3 4 5 6 7 8 9 10]
cap 11: bytes: [1 2 3 4 5 6 7 8 9 10 11]
cap 23: bytes: [1 2 3 4 5 6 7 8 9 10 11 12]
cap 23: bytes: [1 2 3 4 5 6 7 8 9 10 11 12 13]
cap 23: bytes: [1 2 3 4 5 6 7 8 9 10 11 12 13 14]
cap 23: bytes: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
cap 23: bytes: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]

defer 更改 err 的坑

package main

import (
	"fmt"
	"log"
)

func main() {
	defer fmt.Println("Program Exited...")
	log.Printf("1: %v", testDeferErr1())
	log.Printf("2: %v", testDeferErr2())
	log.Printf("3: %v", testDeferErr3())
	log.Printf("4: %v", testDeferErr4())
	log.Printf("5: %v", testDeferErr5())
}

// 此时defer里的error依然是nil
func testDeferErr1() error {
	var err error
	defer log.Printf("defer err %v", err)

	err = fmt.Errorf("TEST ERROR")
	return err
}

// 可以得到返回的err
func testDeferErr2() error {
	var err error
	defer func() {
		log.Printf("defer err %v", err)
	}()

	err = fmt.Errorf("TEST ERROR")
	return err
}

// 但不能改变defer协程外的err
func testDeferErr3() error {
	var err error
	defer func() {
		log.Printf("defer err %v", err)
		err = fmt.Errorf("DEFER ERROR")
	}()

	err = fmt.Errorf("TEST ERROR")
	return err
}

// 注意,这样依然不能改变返回的err
func testDeferErr4() error {
	var err error
	defer func(e *error) {
		log.Printf("defer err %v", *e)
		*e = fmt.Errorf("DEFER ERROR")
	}(&err)

	err = fmt.Errorf("TEST ERROR")
	return err
}

// 只有这样定义才能改变err
func testDeferErr5() (err error) {
	defer func(e *error) {
		log.Printf("defer err %v", *e)
		*e = fmt.Errorf("DEFER ERROR")
	}(&err)

	err = fmt.Errorf("TEST ERROR")
	return

}