坑1. 延迟函数参数实时解析
func main() { a() } func a() { i := 1 defer fmt.Println(i) i++ }
程序执行的最终结果是 1
进阶版:
type X struct { Num int } func main() { i := &X{Num: 2} fmt.Printf("0x0000: %p ", i) defer fmt.Println("0x0050:", *i) defer fmt.Printf("0x0040: %p ", i) defer func() { fmt.Println("0x0032:", i.Num) fmt.Printf("0x0033: %p ", i) }() defer func(p *X) { fmt.Println("0x0031:", i.Num) }(i) defer fmt.Println("0x0030:", i) i.Num++ fmt.Println("0x0010:", i.Num) fmt.Printf("0x0011: %p ", i) i = &X{Num: 99} fmt.Println("0x0021:", i.Num) fmt.Printf("0x0022: %p ", i) }
程序执行的最终结果是
0x0000: 0xc0000a0000 0x0010: 3 0x0011: 0xc0000a0000 0x0021: 99 0x0022: 0xc0000aa000 0x0030: &{3} 0x0031: 99 0x0032: 99 0x0033: 0xc0000aa000 0x0040: 0xc0000a0000 0x0050: {2}
坑2. 延迟函数在匿名返回值和命名返回值函数中的不同表现
func main() { fmt.Println(a()) fmt.Println(b()) } func a() int { var i int defer func() { i++ }() return i } func b() (i int) { defer func() { i++ }() return i }
程序执行的最终结果是 0 1
在a函数中,可以理解成Go自动创建了一个返回值 retValue,相当于执行retValue = i,然后检查是否有defer,如果有则执行,再返回刚才创建的返回值retValue
在b函数中,由于返回值在方法定义时已经被定义,所以没有创建retValue的过程,i就是retValue,defer对于result的修改也会被直接返回
坑3.程序退出时延迟函数不会被执行
func main() { fmt.Println("1") defer fmt.Println("0") os.Exit(0) }
程序执行的最终结果是1
坑4. 字典的操作不是原子操作
有并发需求时使用 sync.Map
坑5, 接口值的返回总是非nil的
底层的interface实现是有一个类型一个内部值,只有在内部值和类型都未设置时(nil, nil),一个接口的值才为 nil
func main() { err := FindALL() if err != nil { fmt.Println(1) } if err.(*MyErr) != nil { fmt.Println(2) } } type MyErr struct { } func (m *MyErr) Error() string { return "" } func FindALL() error { var p *MyErr return p }
坑6. 字符串时注意
删除字符串首尾时使用
strings.TrimPrefix() strings.TrimSuffix()
而不是
strings.TrimLeft() strings.TrimRight()
TrimLeft 从左开始, 如果发现了不在cutset
中的字符, 就从这个点返回
坑7 单例时注意
官方推荐方式为
var once sync.Once once.Do(func() { })
如果同一目录下,要声明两个单例时, once变量不能重复使用
8
https://studygolang.com/articles/2915
注意stop 和 reset
技巧1: JSON解析不输出
结构体增加tag可以在结构体的字段为nil时不输出
`json:"omitempty"`
技巧2: JSON延迟解析
json.RawMessage
坑8. 原生sql.Open 默认只是检查下链接字符串是不是正确的, 要检查是否连接上用connect 或者 open完了之后ping
参考链接:
https://golang.org/doc/faq#atomic_maps