Let’s start with the Go language spec on the zero value.
When memory is allocated to store a value, either through a declaration or a call of
make
ornew
, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type:false
for booleans,0
for integers,0.0
for floats,""
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.
This property of always setting a value to a known default is important for safety and correctness of your program, but can also make your Go programs simpler and more compact. This is what Go programmers talk about when they say “give your structs a useful zero value”.
Here is an example using sync.Mutex
, which is designed to be usable without explicit initialization. The sync.Mutex
contains two unexported integer fields. Thanks to the zero value those fields will be set to will be set to 0
whenever a sync.Mutex
is declared.
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() }
Another example of a type with a useful zero value is bytes.Buffer
. You can decare a bytes.Buffer
and start Read
ing or Write
ing without explicit initialisation. Note that io.Copy
takes an io.Reader
as its second argument so we need to pass a pointer to b
.
package main import "bytes" import "io" import "os" func main() { var b bytes.Buffer b.Write([]byte("Hello world")) io.Copy(os.Stdout, &b) }
A useful property of slices is their zero value is nil
. This means you don’t need to explicitly make
a slice, you can just declare it.
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, " ")) }
Note: var s []string is similar to the two commented lines above it, but not identical. It is possible to detect the difference between a slice value that is nil and a slice value that has zero length. The following code will output false.
package main import "fmt" import "reflect" func main() { var s1 = []string{} var s2 []string fmt.Println(reflect.DeepEqual(s1, s2)) }
A surprising, but useful, property of nil pointers is you can call methods on types that have a nil value. This can be used to provide default values simply.
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()) }
With thanks to Jan Mercl, Doug Landauer, Stefan Nilsson, and Roger Peppe from the wonderful Go+ community for their feedback and suggestions.