4.4. 让零值更有用
假设变量没有初始化,每个变量声明都会自动初始化为与零内存的内容相匹配的值。 这就是零值。 值的类型决定了其零值; 对于数字类型,它为 0
,对于指针类型为 nil
,slices
、map
和 channel
同样是 nil
。
始终设置变量为已知默认值的属性对于程序的安全性和正确性非常重要,并且可以使 Go 语言程序更简单、更紧凑。 这就是 Go 程序员所说的“给你的结构一个有用的零值”。
对于 sync.Mutex
类型。sync.Mutex
包含两个未公开的整数字段,它们用来表示互斥锁的内部状态。 每当声明 sync.Mutex
时,其字段会被设置为 0
初始值。sync.Mutex
利用此属性来编写,使该类型可直接使用而无需初始化。
type MyInt struct {
mu sync.Mutex
val int
}
func main() {
var i MyInt
// i.mu is usable without explicit initialisation.
i.mu.Lock()
i.val++
i.mu.Unlock()
}
另一个利用零值的类型是 bytes.Buffer
。您可以声明 bytes.Buffer
然后就直接写入而无需初始化。
func main() {
var b bytes.Buffer
b.WriteString("Hello, world!\n")
io.Copy(os.Stdout, &b)
}
切片的一个有用属性是它们的零值 nil
。如果我们看一下切片运行时 header
的定义就不难理解:
type slice struct {
array *[...]T // pointer to the underlying array
len int
cap int
}
此结构的零值意味着 len
和 cap
的值为 0
,而 array
(指向保存切片的内容数组的指针)将为 nil
。这意味着你不需要 make
切片,你只需声明它即可。
func main() {
// s := make([]string, 0)
// s := []string{}
var s []string
s = append(s, "Hello")
s = append(s, "world")
fmt.Println(strings.Join(s, " "))
}
注意:
var s []string
类似于它上面的两条注释行,但并不完全相同。值为nil
的切片与具有零长度的切片就可以来相互比较。以下代码将输出false
。
func main() {
var s1 = []string{}
var s2 []string
fmt.Println(reflect.DeepEqual(s1, s2))
}
nil pointers
— 未初始化的指针变量的一个有用属性是你可以在具有 nil
值的类型上调用方法。它可以简单地用于提供默认值。
type Config struct {
path string
}
func (c *Config) Path() string {
if c == nil {
return "/usr/home"
}
return c.path
}
func main() {
var c1 *Config
var c2 = &Config{
path: "/export",
}
fmt.Println(c1.Path(), c2.Path())
}