Go 中的 nil 和 零值(译文) | Go主题月

907 阅读3分钟

Random codewalks

2018年11月6日

作为一名长期从事 java 开发的人,我痴迷于 null 值的检查和处理。可在 Golang 中情况有些不同。在这篇文章中,我将描述一下 nil零值 在 golang 中的用法。

Non-nillable(非空) 和 nillable(空) 类型

在 go 中类型可以是 空 或 非空 。非空类型永远不能是 nil,也永远不会引起 nil-panic(等价于 java 的 nullpointerexception),虽然 go 不像在 java(或其他具有空类型的语言)中有那样,但是在处理空类型时,我们必须小心一点。

Non-nillable 基本类型

在 go 中,基本类型不能为空。像这样:

var a int = nil

无法编译。因为 int 永远不能为 nil。未赋值 int 类型的默认值为 0。运行语句

var a int // default value of int, cannot be nil
fmt.Println(a) // 0

将输出 int 的默认值 0,我们称之为类型的零值。

int 默认为 0 的方式相同,下面这些是其他基本类型及其零值:

基本类型零值
int, int8, int16, int32, int640
uint, uint8, uint16, uint32, uint640
uintptr0
float32, float640.0
byte0
rune0
string"” (empty string)
complex64, complex128(0,0i)
arrays of non-nillable typesarray of zero-values
arrays of nillable typesarray of nil-values

Non-nillable 结构体

组合的 struct 类型也是不可为空的,struct 的默认值将包含其所有字段的默认值。

分析 struct 类型 Person 的代码,

type Person struct {
    Name string
    Age  int
}
var p Person // zero-value of type Person

fmt.Printf("[%#v]\n", p)

当在 main.go 运行这段代码,会打印 [main.Person{Name:"", Age:0}] 。你可以在 golayground 上测试 这段代码

Nillable 类型

更高级的类型是 nillable ,如果它们没有初始化,可能会导致异常。

nillable 类型包括 函数(function)、通道(channel)、切片(slice)、map、接口类型(interface)和指针(pointer)

但是,nil-slice 和 nil-map 仍然可以使用,在使用它们之前不必进行初始化。

nil-maps

如果 map 值为 nil,map 将始终返回该值的零值,这与返回 map 中不存在的 key 时代码一样。代码如下:

var p map[int]string // nil map
fmt.Printf(" %#v  length %d \n",  p[99], len(p))

只需打印 "" length 0 ,键为99的字符串就是 string 值为零。

但是,将值赋给 nil-map ,会异常:

var p map[string]int	// nil map 
p["nils"] = 19 // panic: assignment to entry in nil map

nil-slice

在外部引用切片 will 会导致异常,但是像 len()cap() 这样的操作不会导致异常。它们只返回 0,因为对于未初始化的切片,容量和长度都为零。可以在 nil-slice 上安全地调用 Append 。代码如下:

var p []string // nil slice
fmt.Printf("uninitialized -> %d, %d\n",  len(p), cap(p))
p1 := append(p, "nils") // create a new slice p1 from p
fmt.Printf("after append  -> %d, %d %#v\n",  len(p1), cap(p1), p1)

打印:

uninitialized -> 0, 0
after append  -> 1, 1 []string{"nils"}

goplaygroud

可为 nil 的指针,函数和接口会导致异常

然而,指针和接口类型是 nillable 的。在处理这些类型的问题时,我们必须考虑它们是否为零,以避免异常。例如,这些代码片段不行:


var p *int // pointer to an int
*p++ // panic: runtime error: invalid memory address or nil pointer dereference
// p is an address to nothing, and therefore is nil




var p error // nil-value of type error
error.Error() // panic: runtime error: invalid memory address or nil pointer dereference




var f func(string) // nil-function
f("oh oh") // panic: runtime error: invalid memory address or nil pointer dereference

nil-channel 会永远阻塞

尝试从 nil-channel 读取或写入 nil-channel 将永远阻塞。关闭 nil-channel 会导致异常。

Wrapup

nil 在 go 中有很好的定义。了解什么可以是 nil 以及如何处理不同类型的 nil 值可以增加你对发生的事情的理解,并可以帮助你编写更好的 go 代码。

原文链接:nilsmagnus.github.io/post/nillab…