今天是《有个小问题》系列第一期,我们一起先来看看 Go 语言规范中关于零值的规定。
当通过声明或调用 make 或 new 分配内存来存储一个值时,如果没有提供显式初始化,内存会被赋予一个默认的初始化。这种值的每一个元素都被设置为其类型的零值:布尔值为 false ,整数为 0 ,浮点数为 0.0 ,字符串为 "",指针、函数、接口、切片、 Channel 和 Map 为 nil 。这种初始化是以递归方式进行的,例如,如果没有指定值,结构数组中的每个元素的字段都会被清零。
前言
对于这种总是将一个值设置为已知默认值的属性对于程序的安全性和正确性是很重要的,同时也可以使程序看起来更简单、更紧凑。
这就是 Go 编程者所说的 “给你的代码结构一个有用的零值”。
例子1
下面是一个使用 sync.Mutex 的例子,它被设计成不需要显式初始化就可以使用。sync.Mutex 包含两个未导出的整数字段。由于这些字段将被设置为零值,每当声明 sync.Mutex 时,这些字段将被设置为 0。
package main
import "sync"
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()
}
例子2
另一个有用的零值类型的例子是 bytes.Buffer。可以不进行显式初始化就开始读取或写入,请注意,io.Copy 需要一个 io.Reader 作为它的第二个参数,所以我们需要传递一个指向 b 的指针。
package main
import (
"bytes"
"io"
"os"
)
func main() {
var b bytes.Buffer
b.Write([]byte("Hello world"))
io.Copy(os.Stdout, &b)
}
对于切片来说它们的零值是 nil。这意味着你不需要显式地创建一个切片,你可以直接声明它。
package main
import (
"fmt"
"strings"
)
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 与上面两行注释类似,但不完全相同。这两种初始化切片零值还是有区别的。下面的代码输出 false
package main
import (
"fmt"
"reflect"
)
func main() {
var s1 = []string{}
var s2 []string
// output: false
fmt.Println(reflect.DeepEqual(s1, s2))
}
例子3
nil 指针的零值还有一种比较实用的用法是,对具有 nil 值的类型调用方法,可以用来简单地提供默认值。
package main
import "fmt"
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())
}
关于
关注博客看更多《有个小问题系列》
有疑问加站长微信联系(非本文作者)