Go语言精进之路读书笔记第11条——尽量定义零值可用的类型
11.1 Go类型的零值
Go语言规范中关于变量默认值的描述:
当通过声明或调用new为变量分配存储空间,或者通过复合文字字面量或调用make创建新值,且不提供显式初始化时,Go会为变量或值提供默认值。
Go规范定义的内置原生类型的默认值(零值):
- 所有整型类型:0
- 浮点类型:0.0
- 布尔类型:false
- 字符串类型:""
- 指针、interface、切片(slice)、channel、map、function:nil
Go的零值初始是递归的,即数组、结构体等类型的零值初始化就是对其组成元素逐一进行初始化。
11.2 零值可用
Go从诞生以来就一直秉承着尽量保持“零值可用”的理念,因此我们可以针对符合这种理念的类型的声明了但未初始化的变量,直接调用其类型的方法。
特点
- Go语言零值可用的理念给内置类型、标准库的使用者带来很多便利。不过Go并非所有类型都是零值可用的,并且零值可用也有一定的限制,
比如:
// 1.在append场景下,零值可用的切片类型不能通过下标形式操作数据
var s []int
s[0] = 12 // 报错!
s = append(s, 12) // 正确
// 2.像map这样的原生类型也没有提供对零值可用的支持
var m map[string]int
m["go"] = 1 // 报错!
m1 := make(map[string]int)
m1["go"] = 1 // 正确
// 3.零值可用的类型要注意尽量避免值复制,可以用指针方式来传递值
var mu sync.Mutex
mu1 := mu // 错误: 避免值复制
foo(mu) // 错误: 避免值复制
foo(&mu) // 正确
- 保持与Go一致的理念,给自定义的类型一个合理的零值,并尽量保持自定义类型的零值可用,这样我们的Go代码会更加符合Go语言的惯用法。
标准库里的例子
var p *net.TCPAddr
fmt.Println(p) //输出:<nil>
// 我们看到Go标准库在定义TCPAddr类型及其方法时充分考虑了“零值可用”的理念,使得通过值为nil的TCPAddr指针变量依然可以调用String方法。
/*
func (a *TCPAddr) String() string {
if a == nil {
return "<nil>"
}
ip := ipEmptyString(a.IP)
if a.Zone != "" {
return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port))
}
return JoinHostPort(ip, itoa(a.Port))
}
*/