Loading

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))
}
*/
posted @ 2024-02-01 20:54  brynchen  阅读(15)  评论(0编辑  收藏  举报