Home  >  Article  >  Backend Development  >  Let’s talk about the zero value in Go language. What is its use?

Let’s talk about the zero value in Go language. What is its use?

Golang菜鸟
Golang菜鸟forward
2023-08-08 16:23:011224browse

Background

Hello, everyone, I am asong. Today I will talk to you about the zero value in the Go language. I was a C language enthusiast when I was in college. After working, I felt that the Go language was very similar to the C language, so I chose the Go language. In my work, I will compare some features of the two languages ​​from time to time. Today I want to compare the zero-value feature. Friends who are familiar with the C language know that local variables are not initialized by default in the C language. Uninitialized variables can contain any value, and their use will lead to undefined behavior; if we do not initialize local variables, warning C4700 will be reported at compile time. This warning indicates a Bug, this Bug may cause unpredictable results or malfunctions in the program.There will be no such problem in the Go language. The designers of the Go language have learned some experience in designing the C language, so the zero value specification of the Go language is as follows:

The following content is from the official blog: https://golang.org/ref/spec#The_zero_value

When allocating storage space for a variable through declaration or new call, or through compound literal or When a make call creates a new value, and no explicit initialization is provided, the variable or value is given a default value. Each element of this type of variable or value is set to a zero value for its type: false for boolean type, 0 for numeric type, "" for string type, pointer, function, Interfaces, slices, channels and maps are nil. This initialization is done recursively, i.e. if no value is specified, the fields of each element of the structure array are cleared to zero.

For example, these two simple declarations are equivalent:

var i int 
var i int = 0

or the declaration of this structure:

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

This structuret The zero value of the member field in is as follows:

t.i == 0
t.f == 0.0
t.next == nil

Go This feature in the language that always sets the value to a known default value plays a great role in the safety and correctness of the program. Important role, this also makes the entire Go program simpler and more compact.

零值有什么用

通过零值来提供默认值

我们在看一些Go语言库的时候,都会看到在初始化对象时采用"动态初始化"的模式,其实就是在创建对象时判断如果是零值就使用默认值,比如我们在分析hystrix-go这个库时,在配置Command时就是使用的这种方式:

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

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

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

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

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

 errorPercent := DefaultErrorPercentThreshold
 if config.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,
 }
}

通过零值判断进行默认值赋值,增强了Go程序的健壮性。

开箱即用

为什么叫开箱即用呢?因为Go语言的零值让程序变得更简单了,有些场景我们不需要显示初始化就可以直接用,举几个例子:

  • 切片,他的零值是nil,即使不用make进行初始化也是可以直接使用的,例如:
package main

import (
    "fmt"
    "strings"
)

func main() {
    var s []string

    s = append(s, "asong")
    s = append(s, "真帅")
    fmt.Println(strings.Join(s, " "))
}

但是零值也并不是万能的,零值切片不能直接进行赋值操作:

var s []string
s[0] = "asong真帅"

这样的程序就报错了。

  • 方法接收者的归纳

利用零值可用的特性,我们配合空结构体的方法接受者特性,可以将方法组合起来,在业务代码中便于后续扩展和维护:

type T struct{}

func (t *T) Run() {
  fmt.Println("we run")
}

func main() {
  var t T
  t.Run()
}

我在一些开源项目中看到很多地方都这样使用了,这样的代码最结构化~。

  • 标准库无需显示初始化

我们经常使用sync包中的mutexoncewaitgroup都是无需显示初始化即可使用,拿mutex包来举例说明,我们看到mutex的结构如下:

type Mutex struct {
 state int32
 sema  uint32
}

这两个字段在未显示初始化时默认零值都是0,所以我们就看到上锁代码就针对这个特性来写的:

func (m *Mutex) Lock() {
 // 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()
}

原子操作交换时使用的old值就是0,这种设计让mutex调用者无需考虑对mutex的初始化则可以直接使用。

还有一些其他标准库也使用零值可用的特性,使用方法都一样,就不在举例了。

零值并不是万能

Go语言零值的设计大大便利了开发者,但是零值并不是万能的,有些场景下零值是不可以直接使用的:

  • 未显示初始化的切片、map,他们可以直接操作,但是不能写入数据,否则会引发程序panic:
var s []string
s[0] = "asong"
var m map[string]bool
m["asong"] = true

这两种写法都是错误的使用。

  • 零值的指针

零值的指针就是指向nil的指针,无法直接进行运算,因为是没有无内容的地址:

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

这样才可以:

func main() {
 var p *uint64
 a := uint64(0)
 p = &a
 *p++
 fmt.Println(*p) // 1
}
  • 零值的error类型

error内置接口类型是表示错误条件的常规接口,nil值表示没有错误,所以调用Error方法时类型error不能是零值,否则会引发panic

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

func res() error {
 return nil
}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a6f27]
  • 闭包中的nil函数

在日常开发中我们会使用到闭包,但是这其中隐藏一个问题,如果我们函数忘记初始化了,那么就会引发panic

var f func(a,b,c int)

func main(){
  f(1,2,3) // panic: runtime error: invalid memory address or nil pointer dereference
}
  • 零值channels

我们都知道channels的默认值是nil,给定一个nil channel c:

  • <-c Receiving from c will block forever
  • c <- v Sending a value to c will block forever
  • close(c) Close c and cause panic

Let’s first introduce these scenarios where zero values ​​are unavailable. Only by mastering these can we reduce the frequency of writing bug in daily development.

Summary

Summarize several knowledge points described in this article:

  • GoAll variables or values ​​in the language have default values, which plays a very important role in the safety and correctness of the program
  • Some standard libraries in the Go language use the zero value feature to simplify operations.
  • You can use the "zero value available" feature to improve the efficiency of the code. Structure, make the code simpler and more compact
  • The zero value is not omnipotent. There are some scenarios where the zero value is not available. Please pay attention when developing

The above is the detailed content of Let’s talk about the zero value in Go language. What is its use?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:Golang菜鸟. If there is any infringement, please contact admin@php.cn delete