关于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")
}
初看这个代码会觉得
switch-defer
会在case 0
分支执行完就执行for-defer
会在每一次遍历结束后执行
但实际运行结果如下
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-defer
和for-defer
都是在main
函数返回前执行的,因此可以得出结论:for
和switch
的作用域内的defer
的级别和for
、switch
是相等价的
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
并不是我们预期的0
、1
、2
主要是每次协程都用了同一个变量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
}