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**
。在此示例中,w
是io.Writer
的 interface value ,在w = b
赋值后带有{type: *bytes.Buffer, value: nil}
。因此,w==nil
为false
,因为它携带*byte.Buffer
而不是nil
作为它得具体动态类型。
概要
nil
是一个预先声明的标识符,可用于表示Go中某些类型的零值。- 在比较时使用
nil
时要特别小心,尤其是在涉及interface values
时。需要了解所比较的内容:types, or values
,或两者。(a thing)(nil)
may not equal tonil
, depends on what that thing is (a pointer or an interface). This meansnil
is strong-typed even thoughnil
does not have a default type (sarcasm).(a thing)(nil)
可能不等于nil
,取决于事物是什么(指针或接口)。这意味着nil是强类型的,即使nil没有默认类型(sarcasm)。