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 meansnilis strong-typed even thoughnildoes not have a default type (sarcasm).(a thing)(nil)可能不等于nil,取决于事物是什么(指针或接口)。这意味着nil是强类型的,即使nil没有默认类型(sarcasm)。