【注意】最后更新于 July 9, 2024,文中内容可能已过时,请谨慎使用。
Make the zero value useful。
–Go Proverbs
让我们从 Golang blog 开始吧:The zero value
当内存被分配来存储一个值时,无论是通过声明还是调用 make 或 new,并且没有提供明确的初始化,内存被赋予一个默认的初始化。这种值的每个元素都被设置为其类型的零值 (zero value):布尔值为 false,整数为 0,浮点数为 0.0,字符串为 "",指针、函数、接口、slice、channel 和 map 为 nil。这种初始化是递归进行的,因此,举例来说,如果没有指定值,结构数组的每个元素都将被归零。
这样将一个值设置为零值对程序的安全性和正确性做了很大的保证,同样也能很好的保证程序的可读性与简单性。这也就是 Golang 程序员口中的“让零值更有用 (Make the zero value useful)”。
零值 cheat sheet
| 类型 |
零值 |
| bool |
false |
| int |
0 |
| float |
0.0 |
| string |
"" |
| pointer |
nil |
| function |
nil |
| slice |
nil |
| map |
nil |
| channel |
nil |
同时零值的初始化是递归的,因此,如果没有指定值,结构数组的每个元素都将被归零。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
➜ gore --autoimport
gore version 0.5.3 :help for help
gore> var a [10]int
gore> a
[10]int{
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
}
|
对于结构体也是如此,我们初始化一个引用 A 的 B 结构体,并且没有指定值,那么 B 的每个字段都将被归零。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
➜ gore --autoimport
gore version 0.5.3 :help for help
gore> type A struct { i int; f float64 }
gore> type B struct { i int; f float64; next A }
gore> new(B)
&main.B{
i: 0,
f: 0.000000,
next: main.A{
i: 0,
f: 0.000000,
},
}
|
note:
- new:new(T) 返回一个指向新分配的 T 类型的
零值 的指针。
- 使用的工具为 gore
零值的用法
上文已经介绍了什么是零值,这里我们来看看如何使用它们。
sync.Mutex
这里有一个关于 sync.Mutex 的例子,sync.Mutex 被设计成不用显式地去初始化他就可以直接通过零值来使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package main
import "sync"
type MyInt struct {
mu sync.Mutex
val int
}
func main() {
var i MyInt
// i.mu is usable without explicit initialisation.
i.mu.Lock()
i.val++
i.mu.Unlock()
}
|
得益于零值的特性,Mutex 内部两个未导出的变量都会被初始化为零值。所以 sync.Mutex 的零值是一个未锁定的 Mutex。
1
2
3
4
5
6
7
8
|
// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {
state int32
sema uint32
}
|
bytes.Buffer
另外一个例子是 bytes.Buffer,它的零值是一个空的 Buffer。
1
2
3
4
5
6
7
8
9
10
11
|
package main
import "bytes"
import "io"
import "os"
func main() {
var b bytes.Buffer
b.Write([]byte("go go go"))
io.Copy(os.Stdout, &b)
}
|
JSON omitempty
JSON 接收器也接受 omitempty 这个 flag,当输入的字段是 零值 时,接收器会忽略这个字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
➜ gore --autoimport
gore version 0.5.3 :help for help
gore> type Person struct {
..... Name string `json:"name"`
..... Age int `json:"age"`
..... Addr string `json:"addr,omitempty"`
..... }
gore> p1 := Person{
..... Name: "taoge",
..... Age: 30,
..... }
main.Person{
Name: "taoge",
Age: 30,
Addr: "",
}
gore> data, err := json.Marshal(p1)
...
gore> string(data)
"{\"name\":\"taoge\",\"age\":30}"
|
channel close
在《Channel Axioms》中,也有一条与零值相关的规则,当 channel 关闭时,对被关闭的 channel 做<- 操作,总是立即返回 零值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import "fmt"
func main() {
c := make(chan int, 3)
c <- 1
c <- 2
c <- 3
close(c)
for i := 0; i < 4; i++ {
fmt.Printf("%d ", <-c) // prints 1 2 3 0
}
}
|
解决上述问题的正确的方式是使用 for loop:
1
2
3
4
|
for v := range c {
// do something with v
}
|
map 中未找到对应 key 的 value
对于一个 map,如果没有找到对应的 key,那么这个 map 会返回对应类型一个零值。
1
2
3
4
5
6
7
8
|
➜ gore --autoimport
gore version 0.5.3 :help for help
gore> a := make(map[string]string)
map[string]string{}
gore> a["123"] = "456"
"456"
gore> a["000"]
""
|
解决这个问题的方法是返回多个值:
1
2
3
4
5
6
7
|
gore --autoimport
gore version 0.5.3 :help for help
gore> a := make(map[string]string)
map[string]string{}
gore> c,ok := a["000"]
""
false
|
对于不存在的 key,ok 的 value 将会变成 false。
总结
以上就是关于 零值 的一些经验总结。希望大家在设计代码的时候能够将 零值 更好的用起来,利用 零值 提供的特性来初始化一些变量。
相关链接
文章推荐
最后最后和大家分享一些最近在看的好文,想过用周刊的方式发送但是因为看的比较零散,就放在每篇博文的最后,希望大家能够收获。
文章作者
xiantang
上次更新
2024-07-09
(9ac8718e)