2025-04-11 05:00:04

围棋中的零检测

我在Go中看到了很多检测nil的代码,像这样:

if err != nil { 
    // handle the error    
}

然而,我有一个像这样的结构:

type Config struct {
    host string  
    port float64
}

和config是config的一个实例,当我这样做:

if config == nil {
}

有编译错误,说: 不能将nil转换为Config类型


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

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

config := new(Config) // not nil

or

config := &Config{
                  host: "myhost.com", 
                  port: 22,
                 } // not nil

or

var config *Config // nil

然后你就能检查是否

if config == nil {
    // then
}

除了Oleiade,请参阅零值规范:

When memory is allocated to store a value, either through a declaration or a call of make or new, 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, and nil 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.

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

要检查你的结构是否未初始化,你必须检查它的每个成员 各自的零值(例如host == "", port == 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 }

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

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_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
    } 
}

http://play.golang.org/p/RXcE06chxE


语言规范中提到了比较操作符的行为:

比较运算符

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


可转让性

A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases: x's type is identical to T. x's type V and T have identical underlying types and at least one of V or T is not a named type. T is an interface type and x implements T. x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type. x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type. x is an untyped constant representable by a value of type T.


在Go 1.13及以后的版本中,您可以使用Value。反射包中提供的IsZero方法。

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

除了基本类型,它还适用于Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer和Struct。参考这个。