在《Go語言編程》這本書和不少其餘Go 編程教程中不少都提到過「Go程序員應該讓一些聚合類型的零值也具備意義」的概念,咱們這篇文章主要說一下有意義的零值這個話題。程序員
在 Go 中聲明變量時若是初始化表達式被省略:golang
var 變量名字 類型 = 表達式
複製代碼
那麼將用零值初始化變量。編程
如下是 Go 官方的語言參考對零值初始化機制的解釋:c#
When storage is allocated for a variable, either through a declaration or a call of
new
, or when a new value is created, either through a composite literal or a call ofmake
, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type:false
for booleans,0
for numeric types,""
for strings, andnil
for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.數組
當經過變量聲明、調用
new
函數或者是經過符合字面量([]string{}, structType{}等形式)、調用 make 函數建立新值而且未顯式的提供初始化時,變量或者值將被賦予默認值。變量或者值的每一個元素將被賦予其類型的零值:布爾值爲false,數字類型爲0,字符串爲「」,指針,函數,接口,切片,通道和映射爲nil。該初始化是遞歸完成的,所以,例如,未指定任何值,一個結構體數組的每一個元素的字段都將設置爲字段類型的零值。安全
Go始終將值設置爲已知默認值的特性對於程序的安全性和正確性很重要,也使Go程序更簡單,更緊湊。這就是Go程序員在說「給你的結構體一個有用的零值」時談論的內容。bash
下面是一個使用sync.Mutex
的示例,該示例設計爲無需顯式初始化便可使用。 sync.Mutex
包含兩個未導出的整數字段:app
type Mutex struct {
state int32
sema uint32
}
複製代碼
因爲零值機制的存在,每當聲明sync.Mutex時,這些字段將被設置爲0。ide
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()
}
複製代碼
有用的零值的類型的另外一個示例是bytes.Buffer。你能夠在聲明瞭一個 bytes.Buffer 類型的變量後,無需顯式初始化便可開始讀取或寫入。函數
package main
import "bytes"
import "io"
import "os"
func main() {
var b bytes.Buffer
b.Write([]byte("Hello world"))
io.Copy(os.Stdout, &b)
}
複製代碼
切片類型的零值爲nil。這意味着你無需顯式建立切片,只需聲明它便可。
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, " "))
}
複製代碼
注意:var s [] string
與它上面的兩條註釋行類似,可是不相同。能夠經過程序檢測出nil切片值與具備零長度的切片值之間的差異。如下代碼將輸出false。
package main
import "fmt"
import "reflect"
func main() {
var s1 = []string{}
var s2 []string
fmt.Println(reflect.DeepEqual(s1, s2))
}
複製代碼
對於 nil 指針來講,你可讓你的程序容許在具備nil值的類型上調用方法。這能夠用來簡單地爲方法提供有意義的默認返回值。好比下面的程序在 nil 指針上調用 Path方法是返回了/usr/home
,示例爲了好理解只是簡單輸出了一下調用結果,可是在不少比示例更復雜的功能方法來講這比直接返回 string 的零值空字符對程序更有意義。
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())
}
複製代碼