background

Hello, everyone, I’m Asong. Today, WE will talk about the zero value in Go. When I was in college, I was a C language lover. After working, I felt that Go language was very similar to C language, so I chose to work in Go language. From time to time, I would compare some features of the two languages. Those familiar with C know that local variables are not initialized by default in C. An uninitialized variable can contain any value, and its use results in undefined behavior; If we do not initialize local variables, a warning C4700 is reported at compile time, indicating a Bug that could cause unpredictable results or failures in the program. However, there is no such problem in Go language. The designers of Go language have absorbed some experience in designing C language, so the zero value specification of Go language is as follows:

The following is from the official blog: golang.org/ref/spec#Th…

A default value is given when storage is allocated for a variable through a declaration or new call, or when a new value is created through a compound text or make call, and no explicit initialization is provided. Each element of such a variable or value is set to zero for its type: Boolean false, numeric type 0, string “”, pointer, function, interface, slice, channel, and map nil. This initialization is done recursively; for example, if no value is specified, the field of each element in the structure array is cleared to zero.

For example, these two simple statements are equivalent:

var i int 
var i int = 0
Copy the code

Declaration in or of this structure:

type T struct { i int; f float64; next *T }
t := new(T)
Copy the code

The member fields in this structure t have zero values as follows:

t.i == 0
t.f == 0.0
t.next == nil
Copy the code

The Go language’s ability to always set values to known defaults plays an important role in the security and correctness of programs, and makes the entire Go program simpler and more compact.

What’s the use of zero

Default values are provided by a zero value

When we look at some Go libraries, we will see that we use the “dynamic initialization” mode to initialize objects, which is the default value if the object is zero. For example, when we analyze hystrix-Go, we use this mode to configure the Command:

func ConfigureCommand(name string, config CommandConfig) {
	settingsMutex.Lock()
	defer settingsMutex.Unlock()

	timeout := DefaultTimeout
	ifconfig.Timeout ! =0 {
		timeout = config.Timeout
	}

	max := DefaultMaxConcurrent
	ifconfig.MaxConcurrentRequests ! =0 {
		max = config.MaxConcurrentRequests
	}

	volume := DefaultVolumeThreshold
	ifconfig.RequestVolumeThreshold ! =0 {
		volume = config.RequestVolumeThreshold
	}

	sleep := DefaultSleepWindow
	ifconfig.SleepWindow ! =0 {
		sleep = config.SleepWindow
	}

	errorPercent := DefaultErrorPercentThreshold
	ifconfig.ErrorPercentThreshold ! =0 {
		errorPercent = config.ErrorPercentThreshold
	}

	circuitSettings[name] = &Settings{
		Timeout:                time.Duration(timeout) * time.Millisecond,
		MaxConcurrentRequests:  max,
		RequestVolumeThreshold: uint64(volume),
		SleepWindow:            time.Duration(sleep) * time.Millisecond,
		ErrorPercentThreshold:  errorPercent,
	}
}
Copy the code

The robustness of the Go program is enhanced by default value assignment through zero judgment.

Out of the box

Why is it called out of the box? Because the zero value of Go language makes the application easier, some scenarios can be used directly without display initialization. Here are a few examples:

  • Slice. His zero isnilEven if notmakeInitialization can also be used directly, for example:
package main

import (
    "fmt"
    "strings"
)

func main(a) {
    var s []string

    s = append(s, "asong")
    s = append(s, "Handsome")
    fmt.Println(strings.Join(s, ""))}Copy the code

However, zero is not a panacea. Zero slice cannot be directly assigned:

var s []string
s[0] = "Asong really handsome"
Copy the code

Such a program would report an error.

  • Summary of method recipients

Using the zero-value available feature, and the method acceptor feature of the empty structure, we can combine methods to facilitate subsequent extension and maintenance in business code:

type T struct{}

func (t *T) Run(a) {
  fmt.Println("we run")}func main(a) {
  var t T
  t.Run()
}
Copy the code

I’ve seen this used a lot in open source projects, and it’s the most structured code.

  • The library does not need to display initialization

We often use mutex, once, and Waitgroup in sync packages without display initialization. For example, we can see the structure of mutex:

type Mutex struct {
	state int32
	sema  uint32
}
Copy the code

Both fields default to zero when not initialized, so we can see that the lock code is written for this feature:

func (m *Mutex) Lock(a) {
	// Fast path: grab unlocked mutex.
	if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
		if race.Enabled {
			race.Acquire(unsafe.Pointer(m))
		}
		return
	}
	// Slow path (outlined so that the fast path can be inlined)
	m.lockSlow()
}
Copy the code

The old value used for atomic operations is 0, which allows mutex callers to use it without thinking about mutex initialization.

There are other standard libraries that use zero-available features in the same way.

Zero is not a panacea

The zero value design of Go language greatly facilitates developers, but zero value is not a panacea, there are some scenarios in which zero value can not be used directly:

  • Do not show initialized slices, map, they can directly operate, but can not write data, otherwise it will cause panic:
var s []string
s[0] = "asong"
var m map[string]bool
m["asong"] = true
Copy the code

Both of these are incorrect uses.

  • A pointer to zero

A pointer to nil is a pointer to nil and cannot be evaluated directly because there is no content free address:

var p *uint32
*p++ // panic: panic: runtime error: invalid memory address or nil pointer dereference
Copy the code

In this way:

func main(a) {
	var p *uint64
	a := uint64(0)
	p = &a
	*p++
	fmt.Println(*p) / / 1
}
Copy the code
  • A zero-value error type

The built-in error interface type is a normal interface that represents error conditions. Nil means that there is no error, so the error type cannot be zero when the error method is called, otherwise panic will occur:

func main(a) {
	rs := res()
	fmt.Println(rs.Error())
}

func res(a) error {
	return nil
}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a6f27]
Copy the code
  • Nil functions in closures

We use closures in everyday development, but there is a hidden problem. If our function forgets to initialize it, it can cause panic:

var f func(a,b,c int)

func main(a){
  f(1.2.3) // panic: runtime error: invalid memory address or nil pointer dereference
}
Copy the code
  • Zero channels

We all know that the default value for Channels is nil, given a nil channel c:

  • <-ccReception will block forever
  • c <- vSend the value tocWill block forever
  • close(c)Shut downctriggerpanic

Here are some examples of scenarios where zero values are not available so that you can reduce the frequency of writing bugs in your daily development.

conclusion

Summarize several knowledge points described in this article:

  • GoAll variables or values in the language have default values, which play a very important role in the security and correctness of the program
  • GoSome standard libraries in the language make use of the zero-value feature to simplify operations
  • You can take advantage of the “zero value available” feature to make your code more structured, simpler, and more compact
  • Zero is not a panacea. There are some scenarios where zero is not available

Welcome to our official account: [Golang Dream Factory]

Recommended previous articles:

  • Learning channel design: From getting started to giving up
  • Detail memory alignment
  • Be careful not to abuse Goroutine
  • Source analysis panic and recover, do not understand you hit me!
  • Interviewer: Matsuko is here to talk about memory escape
  • Interviewer: What is the result of two nil comparisons?
  • How does Go manipulate Kafka to guarantee no message loss