iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
Modern Web

還喝不 go系列 第 15

[13th][Day15] nil

nil 空值

什麼? nil 是一種資料型態? 還是 nil 值? 你在跟我開玩笑嗎?

不只是 golang ... 在每個語言中 nil 都是非常重要的,它代表的是空。
而 ... 在不同語言中,「空」的概念都有些細微的不同。

比方說 ...
在 scheme 中,nil 是 true 的!
在 C 中 NULL 跟 0 是等價的。

昨天說到 golang 的任何變數型態在宣告時,都對應到一個初值

false for booleans
0 for numeric
"" for strings
nil for pointers, functions, interfaces, slices, channels, and maps

golang 中的 nil 是不同於其他語言的,為了理解 nil,我們來一點一點、慢慢地揭開 nil 神秘的面紗

nil 是不能被比較的,沒有這種東西:

if nil == nil

nil 可以出現在 if 的右邊,常用於錯誤判斷

if err != nil {
    log.println(err)
    return
}

不同資料型態的 nil 的 address 是一樣的

	var m map[int]string
	var ptr *int
	fmt.Printf("%p", m)
	fmt.Println("")
	fmt.Printf("%p", ptr)

輸出為

0x0
0x0
m 是一個 map,ptr 是一個 pointer
但 ... m 和 ptr 的 address 都是 0x0

nil 是 map,slice,pointer,channel,func,interface 的初值

	var m map[int]string
	var ptr *int
	var c chan int
	var sl []int
	var f func()
	var i interface{}
	fmt.Printf("%#v\n", m)
	fmt.Printf("%#v\n", ptr)
	fmt.Printf("%#v\n", c)
	fmt.Printf("%#v\n", sl)
	fmt.Printf("%#v\n", f)
	fmt.Printf("%#v\n", i)

輸出為
map[int]string(nil)
(*int)(nil)
(chan int)(nil)
[]int(nil)
(func())(nil)

注意:struct 的初值不是 nil,而是該 struct 旗下,每個變數自己的初值。且不能將 struct 類型和 nil 進行比較,會跳紅字,無法編譯

nil 不是 golang 的關鍵字
而且 ... nil 的值可以被更改(下面是個不好的示範

	nil := 123
	fmt.Println(nil) // 123

	// The following line fails to compile,
	// for nil represents an int value now
	// in this scope.
	var _ map[string]int = nil

純屬娛樂,請勿這樣使用,如果造成其他不可預期的後遺症 ...
請不要來找我(X

PS: null 以及 NULL 在許多其他語言也不是關鍵字


不同資料型態的 nil 值的大小可能會有所不同
同型態變數所佔的記憶體都是相同的,nil 值佔記憶體的大小始終與類型與 nil 值相同的非 nil 值的大小相同。
but ...,不同變數型態的 nil 的記憶體可能具有不同的大小。

	var p *struct{} = nil
	fmt.Println( unsafe.Sizeof( p ) ) // 8

	var s []int = nil
	fmt.Println( unsafe.Sizeof( s ) ) // 24

	var m map[int]bool = nil
	fmt.Println( unsafe.Sizeof( m ) ) // 8

	var c chan string = nil
	fmt.Println( unsafe.Sizeof( c ) ) // 8

	var f func() = nil
	fmt.Println( unsafe.Sizeof( f ) ) // 8

	var i interface{} = nil
	fmt.Println( unsafe.Sizeof( i ) ) // 16

對 golang 來說,相同變數型態的兩個值的大小 nil 始終相同。
ex:所有不同的 slice 來說,當 slice 為空時,其 nil 值的大小都相同。

nil 是 golang 中一個熟悉而重要的 default 值。它是很多資料型態 零值(zero values)的表示方式。 很多有一些其他語言轉 go 的開發人員視 nil 為他語言 null (或者 NULL) 的複製品

恩 ... 有部分是正確的,但是 Go 中的 nil 和其他語言的 null(或者 NULL)有很多不同之處。
ex : 我們可以直接使用 nil 而不用宣告他。

理解 nil 是學習 golang 一個很重要的部分。

nil 之所以比較難以理解,是因為我們經常搞混 nil value 和 nil type 的區別,value vs type 當然是不一樣的哦 >.^

從上面 nil slice和 empty slice 的不同 延伸,其實 empty slice 也可以作為 slice 的 zero value。特地發明一個 nil 值,應該是 golang 出於對性能的考虑。

nil pointer 其實是一切 nil 值的根本型態,可以使用的部分就這麼多
能省下的記憶體就先省下來,pointer 就先讓他 nil 著。

if err != nil {
panic(err)
}

Go 有自己特殊的錯誤處理方式(例如 Java 使用 try..catch),正因為錯誤發生時,是傳回錯誤,所以有許多方式可以檢查錯誤

nils in Go


上一篇
[13th][Day14] map
下一篇
[13th][Day16] tamplete
系列文
還喝不 go20

尚未有邦友留言

立即登入留言