Go中的nil检测

huangapple go评论93阅读模式
英文:

nil detection in Go

问题

我看到很多用Go语言编写的代码来检测nil,就像这样:

if err != nil {
    // 处理错误
}

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

type Config struct {
    host string
    port float64
}

并且config是Config的一个实例,当我这样做时:

if config == nil {
}

会出现编译错误,提示:

无法将nil转换为Config类型
英文:

I see a lot of code in Go to detect nil, like this:

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

however, I have a struct like this:

type Config struct {
    host string  
    port float64
}

and config is an instance of Config, when I do:

if config == nil {
}

there is compile error, saying:
cannot convert nil to type Config

答案1

得分: 218

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

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

config := new(Config) // 非nil

或者

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

或者

var config *Config // nil

然后你就可以检查

if config == nil {
    // 然后
}
英文:

The compiler is pointing the error to you, you're comparing a structure instance and nil. They're not of the same type so it considers it as an invalid comparison and yells at you.

What you want to do here is to compare a pointer to your config instance to nil, which is a valid comparison. To do that you can either use the golang new builtin, or initialize a pointer to it:

config := new(Config) // not nil

or

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

or

var config *Config // nil

Then you'll be able to check if

if config == nil {
    // then
}

答案2

得分: 69

除了Oleiade之外,还可以参考零值规范

当分配内存来存储一个值时,无论是通过声明还是通过调用make或new,如果没有提供显式的初始化,内存将被赋予默认初始化。**这样一个值的每个元素都被设置为其类型的零值:布尔类型为false,整数类型为0,浮点数类型为0.0,字符串类型为"",指针、函数、接口、切片、通道和映射类型为nil。**这种初始化是递归进行的,因此,例如,如果没有指定值,结构体数组的每个元素的字段将被置零。

正如你所看到的,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 }
英文:

In addition to Oleiade, see the spec on zero values:

> 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.

As you can see, nil is not the zero value for every type but only for pointers, functions, interfaces, slices, channels and maps. This is the reason why config == nil is an error and
&config == nil is not.

To check whether your struct is uninitialized you'd have to check every member for its
respective zero value (e.g. host == "", port == 0, etc.) or have a private field which
is set by an internal initialization method. Example:

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

得分: 22

我已经创建了一些示例代码,使用多种方式创建新变量。前三种方式似乎是创建值,而后两种方式是创建引用。

package main

import "fmt"

type Config struct {
    host string
    port float64
}

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

    // 引用
    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}
英文:

I have created some sample code which creates new variables using a variety of ways that I can think of. It looks like the first 3 ways create values, and the last two create references.

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

which outputs:

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

答案4

得分: 9

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

if reflect.ValueOf(v).IsZero() {
    // v是零值,做一些操作
}

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

英文:

In Go 1.13 and later, you can use Value.IsZero method offered in reflect package.

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

Apart from basic types, it also works for Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer, and Struct. See this for reference.

答案5

得分: 7

你也可以像这样检查 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") // 不会打印
    }

    if b == (A{}) {
        fmt.Println("B is empty") // 会打印
    }
}
英文:

You can also check like struct_var == (struct{}). This does not allow you to compare to nil but it does check if it is initialized or not. Be careful while using this method. If your struct can have zero values for all of its fields you won't have great time.

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

答案6

得分: 3

语言规范中提到了比较运算符的行为:

比较运算符

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


可赋值性

在以下任何情况下,值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的值表示的无类型常量。
英文:

The language spec mentions comparison operators' behaviors:

comparison operators
> In any comparison, the first operand must be assignable to the type
> of the second operand, or vice versa.


Assignability

> 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.

huangapple
  • 本文由 发表于 2013年11月27日 18:41:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/20240179.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定