原文鏈接:http://www.zhoubotong.site/post/45.html
開箱即用
什麼叫開箱即用呢?因爲Go
語言的零值讓程序變得更簡單了,有些場景我們不需要顯示初始化就可以直接用,舉幾個例子:
切片,他的零值是nil
,即使不用make
進行初始化也是可以直接使用的,例如:
package main import ( "fmt" "strings" ) func main() { var s []string s = append(s, "love") s = append(s, "遊戲") fmt.Println(strings.Join(s, " ")) // love 遊戲 }
但是零值也並不是萬能的,零值切片不能直接進行賦值操作:
var s []string s[0] = "love 遊戲"
這樣的程序就報錯了。
-
方法接收者的歸納:利用零值可用的特性,我們配合空結構體的方法接受者特性,可以將方法組合起來,在業務代碼中便於後續擴展和維護:
package main import ( "fmt" ) type T struct{} func (t *T) Run() { fmt.Println("love 遊戲") } func main() { var t T t.Run() }
我在一些開源項目中看到很多地方都這樣使用了,這樣的代碼最結構化。
零值並不是萬能
Go
語言零值的設計大大便利了開發者,但是零值並不是萬能的,有些場景下零值是不可以直接使用的:
-
未顯示初始化的切片、map,他們可以直接操作,但是不能寫入數據,否則會引發程序panic:
func main() { var s []string s[0] = "周伯通" // panic:runtime error: index out of range [0] with length 0 var m map[string]bool m["love"] = true // panic:assignment to entry in nil map fmt.Println(s, m) }
這兩種寫法使用都是錯誤的。
零值的指針
-
零值的指針就是指向
nil
的指針,無法直接進行運算,因爲是沒有無內容的地址:func main() { var p *uint32 *p++ fmt.Println(p) //panic: runtime error: invalid memory address or nil pointer dereference }
改成這樣纔可以
package main import ( "fmt" ) func main() { var p *uint64 a := uint64(0) p = &a *p++ fmt.Println(*p) // 1 }
零值的error類型
error內置接口類型是表示錯誤條件的常規接口,nil值表示沒有錯誤,所以調用Error
方法時類型error
不能是零值,否則會引發panic
:
package main import ( "fmt" ) func main() { res := response() fmt.Println(res.Error()) //panic: runtime error: invalid memory address or nil pointer dereference } func response() error { return nil }
閉包中的nil函數
在日常開發中我們會使用到閉包,但是這其中也隱藏了一個問題,如果我們函數忘記初始化了,那麼就會引發panic
:
package main var fun func(a, b, c int) func main() { fun(1, 2, 3) // panic: runtime error: invalid memory address or nil pointer dereference }
怎麼解決呢?可以使用帶參數閉包或者不帶參數的閉包,以下作爲參考示例:
package main import "fmt" func main() { //先調用閉包外面的方法傳給變量 add_func := addNumber(1, 2) //再調用裏面的方法,因爲有了i++ 同一個內存地址 在一次編譯中i的值會迭代加1 fmt.Println(add_func(1, 1)) //1 3 2 fmt.Println(add_func(0, 0)) //2 3 0 fmt.Println(add_func(2, 2)) //3 3 4 } // 閉包使用方法,定義add的傳參 和函數差不多,再定義fun 可理解匿名函數 func addNumber(x1, x2 int) func(x3 int, x4 int) (int, int, int) { i := 0 // 這裏需要對匿名函數return 理解調用add回調下func,再傳參 return func(x3 int, x4 int) (int, int, int) { i++ //最後return出 return i, x1 + x2, x3 + x4 } }
package main import "fmt" func main() { /* add 爲一個函數,函數 i 爲 0 */ nextNumber := addNumber() /* 調用 add 函數,i 變量自增 1 並返回 */ fmt.Println(nextNumber()) //1 fmt.Println(nextNumber()) //2 fmt.Println(nextNumber()) //3 } func addNumber() func() int { i := 0 return func() int { i++ return i } }
關於零值不可用的場景先介紹這些,掌握這些才能在日常開發中減少寫bug
的頻率。
總結
總結一下本文敘說的幾個知識點:
-
Go
語言中所有變量或者值都有默認值,對程序的安全性和正確性起到了很重要的作用。 -
Go
語言中的一些標準庫利用零值特性來實現,簡化操作。 -
可以利用"零值可用"的特性可以提升代碼的結構化、使代碼更簡單、更緊湊。
-
零值也不是萬能的,有一些場景下零值是不可用的,開發時要注意。