Go中的零检测

Go中的零检测,go,null,Go,Null,我在Go to detect nil中看到很多代码,如下所示: if err != nil { // handle the error } type Config struct { host string port float64 } 但是,我有这样一个结构: if err != nil { // handle the error } type Config struct { host string port fl

我在Go to detect nil中看到很多代码,如下所示:

if err != nil { 
    // handle the error    
}
type Config struct {
    host string  
    port float64
}
但是,我有这样一个结构:

if err != nil { 
    // handle the error    
}
type Config struct {
    host string  
    port float64
}
config是config的一个实例,当我执行以下操作时:

if config == nil {
}
出现编译错误,说明:
无法将nil转换为Config类型

编译器将错误指向您,您正在比较结构实例和nil。它们不是同一类型的,所以它认为这是一个无效的比较,并对你大喊大叫

这里要做的是将指向配置实例的指针与nil进行比较,这是一个有效的比较。为此,您可以使用golang new builtin,或初始化指向它的指针:

config := new(Config) // not nil

然后你就可以检查一下

if config == nil {
    // then
}

除Oleiade外,请参见:

当通过声明或调用make或new分配内存来存储值,并且没有提供显式初始化时,将为内存提供默认初始化此类值的每个元素的类型都设置为零值:布尔值为false,整数为0,浮点值为0.0”,“字符串”为0,指针、函数、接口、切片、通道和映射为nil。此初始化是递归完成的,例如,如果没有指定值,结构数组的每个元素的字段都将为零

正如您所看到的,
nil
不是每种类型的零值,而只是指针、函数、接口、切片、通道和映射的零值。这就是为什么
config==nil
是一个错误并且
&config==nil
不是

要检查结构是否未初始化,必须检查每个成员的 相应的零值(例如,
主机==”
端口==0
,等等)或有一个私有字段 由内部初始化方法设置。例如:

type Config struct {
    Host string  
    Port float64
    setup bool
}

func NewConfig(host string, port float64) *Config {
    return &Config{host, port, true}
}

func (c *Config) Initialized() bool { return c != nil && c.setup }

我已经创建了一些示例代码,它使用我能想到的各种方法创建新变量。看起来前三种方法创建值,后两种方法创建引用

package main

import "fmt"

type Config struct {
    host string
    port float64
}

func main() {
    //value
    var c1 Config
    c2 := Config{}
    c3 := *new(Config)

    //reference
    c4 := &Config{}
    c5 := new(Config)

    fmt.Println(&c1 == nil)
    fmt.Println(&c2 == nil)
    fmt.Println(&c3 == nil)
    fmt.Println(c4 == nil)
    fmt.Println(c5 == nil)

    fmt.Println(c1, c2, c3, c4, c5)
}
哪些产出:

false
false
false
false
false
{ 0} { 0} { 0} &{ 0} &{ 0}

您还可以像
struct\u var==(struct{})
一样进行检查。这不允许您与nil进行比较,但它会检查它是否已初始化。使用这种方法时要小心。如果您的结构可以为其所有的字段都提供数据,那么您将不会有很好的时间

package main

import "fmt"

type A struct {
    Name string
}

func main() {
    a := A{"Hello"}
    var b A

    if a == (A{}) {
        fmt.Println("A is empty") // Does not print
    } 

    if b == (A{}) {
        fmt.Println("B is empty") // Prints
    } 
}

提到比较操作员的行为:

在任何比较中,第一个操作数必须可分配给类型 第二个操作数的值,反之亦然


值x可分配给类型为T的变量(“x可分配给 T”)在任何此类情况下:

  • x的类型与T相同
  • x的类型V和T具有相同的底层类型,并且V或T中至少有一个不是命名类型
  • T是一种接口类型,x实现T
  • x是双向通道值,T是通道类型,x的类型V和T具有相同的元素类型,并且V或T中至少有一个不是 命名类型
  • x是预先声明的标识符nil,T是指针、函数、片、映射、通道或接口类型
  • x是一个非类型常数,可由T型值表示

在Go 1.13及更高版本中,您可以使用
Value.IsZero
方法,该方法在
reflect
包中提供

if reflect.ValueOf(v).IsZero() {
    // v is zero, do something
}

除了基本类型外,它还适用于数组、Chan、Func、接口、Map、Ptr、Slice、UnsafePointer和Struct。请参阅以获取参考。

进一步了解上述内容,这就是为什么
time.time
有一个
IsZero()
方法。但是,如果config==config{}为您检查所有字段,您也可以这样做,如果config==config{}也可以这样做(结构相等在Go中定义得很好)。然而,如果你有很多字段,这是没有效率的。而且,可能零值是一个正常且可用的值,因此传入一个值并不特别。如果以指针形式访问Config,则初始化的函数将失败。可以将其更改为
func(c*Config)Initialized()bool{return!(c==nil)}
@Sundar在这种情况下,这样做可能比较方便,因此我应用了更改。但是,通常我不希望方法调用的接收端检查自身是否为nil,因为这应该是调用方的工作。我猜
var-config&config//nil
应该是:
var-config*config
var-config*config
崩溃,内存地址无效或零指针解引用。也许我们需要
var-config-config
我理解这个选择背后的原因可能不是你的,但对我来说,“if!(config!=nil)”是有效的,而“if-config==nil”不是。两者都在同一个结构和非结构之间进行比较。@retorquery它们都无效,请参阅。是否是“!=”或者“==”没有区别;区别在于config是一个结构还是指向结构的指针。我认为这是错误的,因为它总是错误的:我不明白为什么端口是float64类型的?它不应该是。Go的JSON api将任何数字从JSON导入到float64,我必须将float64转换为int。