坑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