Go 的零值有什么用?看看這四個(gè)場(chǎng)景
背景
Go 語(yǔ)言中有一個(gè)有些特殊的概念,叫做零值。許多轉(zhuǎn)語(yǔ)言的同學(xué)經(jīng)常會(huì)弄混淆,一開始會(huì)不適應(yīng)。
代碼如下:
func main() {
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)
}
輸出結(jié)果:
0 0 false ""
這會(huì)導(dǎo)致大家在定義各種數(shù)據(jù)庫(kù)字段時(shí)比較糾結(jié),又會(huì)說(shuō) Go 這零值,例如:整型的零值 0,跟數(shù)據(jù)庫(kù)里的 0 枚舉值沖突。又或是做入?yún)⑴袛鄷r(shí)怎么區(qū)別開?
一時(shí)半會(huì)想不明白,為什么 Go 要定義這個(gè)零值來(lái)增加復(fù)雜度?
有什么用
官方詮釋
Go 核心團(tuán)隊(duì)的 @Dave Cheney 在《What is the zero value, and why is it useful?[1]》中對(duì)零值進(jìn)行了詳細(xì)的闡釋,以下部分為該文的展開介紹。
將一個(gè)值設(shè)置為已知默認(rèn)值的特性,對(duì)于程序的安全性和正確性非常重要??梢允鼓愕?Go 程序更加簡(jiǎn)單和緊湊。這就是 Go 程序員常說(shuō)的 "給你的結(jié)構(gòu)一個(gè)有用的零值"。
官方案例
以下是 Go 官方給出的幾個(gè)零值的例子,非常具有代表性。分別是:
- sync.Mutex。
- byte.Buffer。
- slices。
- nil func。
Sync.Mutex
sync.Mutex 被設(shè)計(jì)為無(wú)需顯式初始化就可以使用,可以實(shí)現(xiàn)這個(gè)功能的原因是 sync.Mutex 包 含兩個(gè)未導(dǎo)出的整數(shù)字段。
用大白話講,就是由于零值的存在,只要聲明了 sync.Mutex,這些字段就會(huì)被設(shè)置為 0(值會(huì)被初始化)。
因此可以無(wú)需顯式初始化就可以開箱即用。
如下代碼:
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()
}
Byte.Buffer
因?yàn)橛辛阒档拇嬖?,bytes.Buffer 在進(jìn)行寫入或讀取的操作時(shí),不需要人為的進(jìn)行明確的初始化。也能做到很好的開箱即用。
如下代碼:
package main
import "bytes"
import "io"
import "os"
func main() {
var b bytes.Buffer
b.Write([]byte("Hello world"))
io.Copy(os.Stdout, &b)
}
注:io.Copy 需要一個(gè) io.Reader 作為它的第二個(gè)參數(shù),所以我們需要傳遞一個(gè)指向 b 的指針。
Slices
在 slices 的定義中,它的零值是 nil。這意味著你不需要顯式定義一個(gè) slices,只需要直接聲明它,就可以使用了。
如下:
package main
import "fmt"
import "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, " "))
}
Nil func
你可以在有 nil 值的類型上調(diào)用方法,這也是零值作為缺省值的作用之一。
這個(gè)場(chǎng)景,甚至一度讓許多 Go 程序員以為是 BUG,我還專門寫過(guò)文章。這是可行的,其實(shí)是零值的延伸出來(lái)的用法。
如下代碼:
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())
}
總結(jié)
零值,在 Go 里雖然是一個(gè)缺省值。但它本質(zhì)上不僅僅是一個(gè) “缺省”,它還在 Go 程序里的許多應(yīng)用場(chǎng)景提供了不少的便捷特性,甚至是隱晦的功能。
希望本文對(duì)你對(duì)零值的理解有進(jìn)一步的幫助!