Nil in Go


Nil in Go

Go中 的 nil 是什么

Go中的nil具有以下含义:

  • 它代表Go中的“ null”。这意味着两件事:1.它没有类型。 2.其值为“ null”。
  • 它是Go中预先声明的标识符,这意味着您可以使用它而不必声明它。
  • 它表示Go中某些类型的零值(和默认值),包括 interface types, pointer types, slice types, map types, channel types, function types 。

使用nil 作为零值

nil表示Go中某些类型的零值(和默认值)。

例如:

package main

func main() {
    // Use `nil` as zero values
    _ = interface{}(nil)
    _ = (*struct{})(nil)        // () around *struct{} is necessary, otherwise Go will not be able to know where `*` points to.
    _ = string[](nil)
    _ = map[string]int(nil)
    _ = chan string(nil)
    _ =(func())(nil)            // () around func() is necessary

    // This lines are equivalent to the above lines
    var _ interface{} = nil
    var _ *struct{} = nil
    var _ string[] = nil
    var _ map[string]int = nil
    var _ chan string = nil
    var _ func() = nil

    // This lines are equivalent to the above lines, as `nil` is default values of these types
    var _ interface{}
    var _ *struct{}
    var _ string[]
    var _ map[string]int
    var _ chan string
    var _ func()
}

对比 nil

两种不同类型的两个nil值是不可比较的

例如:

package main

func main() {
    var _ = (*bool)(nil) == (*string)(nil) // compiler error: mismatched types.
    var _ = chan int(nil) == chan bool(nil) // compiler error: mismatched types.
}

当他们试图比较两种不同类型的nil值时,这段代码将无法编译。

两个相同类型的nil值可能无法比较

例如:

package main

func main() {
    var _ = ([]string)(nil) == ([]string)(nil)                  // compiler error: invalid operation.
    var sb = (map[string]bool)(nil) == (map[string]bool)(nil)   // compiler error: invalid operation.
    var _ = (func())(nil) == (func())(nil)                      // compiler error: invalid operation.
}

var sb = (map[string]bool)(nil) == (map[string]bool)(nil) 为例,同一类型 ( map[string]bool) 的两个nil值不可比较的原因是Go不支持切片、映射和函数类型的比较。 在本例中,我们正在比较一个不可比较类型的两个值,从而导致失败。

但以下代码有效,结果是true

    var _ = ([]string)(nil) == nil              // true
    var sb = (map[string]bool)(nil) == nil      // true
    var _ = (func())(nil) == nil                // true

var sb = (map[string]bool)(nil) == nil 为例, (map[string]bool)(nil) 声明一个临时变量 map[string]bool 其值为 nil(map[string]bool)(nil) == nil 检测变量的值是否为 nil 然后将结果分配给 sb. 可以看到,在本例中,我们将不可比较类型的值与其零值(nil)进行比较。这就是它起作用的原因

只有当该类型支持比较时,同一类型的两个nil值才能进行比较

例如:

package main
import "fmt"

func main() {
    fmt.Println( interface{}(nil) == interface{}(nil) ) // true
    fmt.Println( (*int)(nil) == (*int)(nil) )           // true
    fmt.Println( chan string(nil) == chan string(nil) ) // true
}
当涉及Interface 时,在 nil比较中要小心

下面的代码不会导致任何编译器报错,但结果是false而不是true

package main
import "fmt"

func main() {
    fmt.Println( interface{}(nil) == (*int)(nil) )      // false
}

Explanation:

  • 接口值由动态类型和动态值组成。 interface{}(nil) 使用 {type: nil, value: nil}声明一个接口值。

  • 在与接口值进行比较之前,将非接口值转换为接口值的类型。在此示例中,(*int)(nil)使用 {type: *int, value: nil}转换为接口值。

  • 两个nil接口值只有在携带相同类型时才是等价的。在本例中,转换后的接口值 {type: *int, value: nil} 具有具体的动态类型,但另一个接口值没有。这就是为什么比较结果是false的。

一个更有意思的例子:

package main

import "io"
import "bytes"
import "fmt"

func main() {
    var w io.Writer
    fmt.Println(w == nil)   // True

    var b *bytes.Buffer
    w = b
    fmt.Println(w == nil)   // false

    write(w)                // panic: runtime error: invalid memory address or nil pointer dereference
}

// If out is non-nil, output will be written to it.
//
func write(out io.Writer) {
    // ...do something...
    if out != nil {                     // This guard is not secure enough
        out.Write([]byte("done!\n"))
    }
}

Explanation:

  • 接口值仅在其类型和值均为 **nil**时才等于 **nil**。在此示例中, wio.Writer的 interface value ,在w = b赋值后带有 {type: *bytes.Buffer, value: nil}。因此, w==nilfalse,因为它携带 *byte.Buffer 而不是nil作为它得具体动态类型。

概要

  • nil是一个预先声明的标识符,可用于表示Go中某些类型的零值。
  • 在比较时使用nil时要特别小心,尤其是在涉及 interface values时。需要了解所比较的内容:types, or values,或两者。
  • (a thing)(nil) may not equal to nil, depends on what that thing is (a pointer or an interface). This means nil is strong-typed even though nildoes not have a default type (sarcasm).
  • (a thing)(nil)可能不等于nil,取决于事物是什么(指针或接口)。这意味着nil是强类型的,即使nil没有默认类型(sarcasm)。

 上一篇
解决keepalived脑裂问题 解决keepalived脑裂问题
一.介绍脑裂(split-brain):指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,例如都去用同一个ip提供网页服务,结果会导致系统混乱,数据损坏。 对
2020-12-27
下一篇 
Kubernetes on CRI-O (CentOS) Kubernetes on CRI-O (CentOS)
Kubernetes on CRI-O (CentOS)Docker 现在在 Kubernetes 中已弃用 。kubelet 中的 Docker 支持现已弃用,并将在以后的版本中删除。Kubelet 使用一个名为 dockershim的模
2020-12-15
  目录