1. Go 类型的零值
当通过声明或调用new为变量分配存储空间,或者通过复合文字字面量或make调用创建新值, 并且还不提供显式初始化的情况下,Go会为变量或值提供默认值。
Go 语言的每种原生类型都有其默认值,这个默认值就是这个类型的零值。下面是 Go 规范定义的内置原生类型的默认值(零值)。
所有整型类型:0浮点类型:0.0布尔类型:false字符串类型:""指针、interface、slice、channel、map、function:nil
- Go 的零值初始是递归的,即诸如数组、结构体等类型的零值初始化就是对其组成元素逐一进行零值初始化。
2. 零值可用
第一个例子是关于 slice 的:
var zeroSlice []intzeroSlice = append(zeroSlice, 1)fmt.Println(zeroSlice) // 输出:[1]
按传统的思维,对于值为 nil 这样的变量我们要给其赋上合理的值后才能使用。但是 Go 具备零值可用的特性,我们可以直接对其使用 append 操作,并且不会出现引用 nil 的错误。
第二个例子是通过 nil 指针调用方法的:
// callmethodthroughnilpointer.gopackage mainimport ("fmt""net")func main() {var p *net.TCPAddrfmt.Println(p) //输出:<nil>}
我们在标准输出上输出该变量,fmt.Println 会调用 p.String()。我们来看看 TCPAddr 这个类型的 String 方法实现:
// $GOROOT/src/net/tcpsock.gofunc (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))}
sync.Mutex:
var mu sync.Mutexmu.Lock()mu.Unlock()
bytes.Buffer:
// bytesbufferwrite.gopackage mainimport ("bytes")func main() {var b bytes.Bufferb.Write([]byte("Effective Go"))fmt.Println(b.String()) // 输出:Effective Go}
3. 小结
没有提供零值可用的例子:
var s []ints[0] = 12 // 报错!s = append(s, 12) // OK
var m map[string]intm["tonybai"] = 1 // 报错!m1 := make(map[string]intm1["tonybai"] = 1 // OK
注意尽量避免值拷贝:
var mu sync.Mutexmu1 := mu // Error: 避免值拷贝foo(mu) // Error: 避免值拷贝
