前陣子在網上看到一些關於Go比較不錯的小知識點,下面總結下分享給你們。面試
new和make都是Go中用來建立對象用的,new這個關鍵字在不少編程語言裏都有,好比在C++和Java裏,均可以用new來建立對象。編程
在Go中,new的做用一樣是用來建立對象,好比new(T)將會爲T建立對象,並同時將這個對象賦一個零值,而後返回T對象的指針*T,下面咱們演示用3種不一樣的辦法建立bytes.Buffer的對象,並返回它的指針。後端
// 聲明一個變量,而後取它的地址並賦值給指針p var buf bytes.Buffer p := &buf // 使用複合聲明的方式一步完成 p := &bytes.Buffer{} // 使用new也是一步完成 p := new(bytes.Buffer) 複製代碼
上面3種建立對象的方法都是等價的。bash
make在平常的編程中也很經常使用,可是它的使用範圍僅限於在slice,map,channel中。使用make也是建立對象,好比make(T),但它返回的是對象的值T,回顧下make的用法。微信
// 建立一個長度爲0,容量爲8的slice sl := make([]string, 0, 8) // 建立一個阻塞的channel ch := make(chan int) // 建立一個map m := make(map[string]string) 複製代碼
上面使用make建立的對象返回的都是對應的值類型。markdown
1. new返回的是T的指針,make返回的是T的值。app
2. make僅能用於建立slice,map,channel。編程語言
對於變量命名,Go大師Dave Cheney舉了個頗有趣比喻:你給變量命名就像給你家的寵物取名同樣,名字上不要帶上「xx狗」,」xx貓「,由於你們都能知道它是狗仍是貓。函數
因此你的變量名應該是描述變量的內容,而不是描述變量的類型,看看如下的寫法。oop
var usersMap map[string]*User 複製代碼
usersMap這個變量名看起來還不錯,描述的是*User的map映射類型。可是Go是一門靜態語言,咱們給一個map取變量名的時候是不須要像動態語言同樣,由於怕賦值錯誤的類型而給它加上類型的,所以這個Map後綴是多餘的。
咱們再以這種方式來命名幾個變量。
var ( companiesMap map[string]*Company productsMap map[string]*Products ) 複製代碼
如今,咱們已經命名了3個map類型的變量:usersMap,companiesMap,productsMap ,其中它們對應的value值都是不一樣的struct類型。當咱們將*User賦值productsMap的時候,這時候編譯器是會報錯的,不像動態語言那樣只能在運行時纔會報錯。
這種狀況下,加上Map後綴並無更好的描述這個變量,反而還只是一個多餘的後綴。因此不建議在變量名中帶有類型。
一樣的在方法命名上也相似。
type Config struct { // } func (c *Config) WriteConfig(w io.Writer) { // } // would be better func (c *Config) Write(w io.Writer) { // } 複製代碼
上面代碼中,由於WriteConfig是*Config的方法,因此Config後綴也是多餘的。
另外,包名最好別佔用類型的名字,就像context包裏的Context類型,當咱們引入這個context包的時候,只能用相似ctx這種變量名,而不是context。
// 這樣命名就很奇怪,並且十分很差看
func WriteLog(context context.Context, message string)
// 只能以這樣的形式取名
func WriteLog(ctx context.Context, message string)
複製代碼
Go的變量命名宗旨是簡潔明瞭,變量名應該獨立於它的類型。
想象下你寫的代碼中,使用到的一個函數會panic可能會panic,並且你還改不了那個函數,像這樣:
func pressButton() { fmt.Println("I'm Mr. Meeseeks, look at me!!") // other stuff then happens, but if Jerry asks to // remove 2 strokes from his golf game... panic("It's gettin' weird!") } 複製代碼
雖然這個函數會panic,但你仍是不得不使用它,當它panic的時候咱們能夠捕獲這個錯誤,就像這樣:
func doStuff() error { var err error // If there is a panic we need to recover in a deferred func defer func() { if r := recover(); r != nil { err = errors.New("the meeseeks went crazy!") } }() pressButton() return err } 複製代碼
當pressButton發生panic的時候,咱們會覺得將會返回一個error,但結果是返回一個nil。是由於pressButton發生panic了就直接返回了,並不會走到return err。
想修復這個問題很簡單,只需給error起一個變量名就行了。
func doStuff() (err error) { // If there is a panic we need to recover in a deferred func defer func() { if r := recover(); r != nil { err = errors.New("the meeseeks went crazy!") } }() pressButton() return err } 複製代碼
1.《理解 Go make 和 new 的區別》 sanyuesha.com/2017/07/26/…
2.《Using named return variables to capture panics in Go》 www.calhoun.io/using-named…
3.dave.cheney.net/2019/01/29/…
感謝閱讀,歡迎你們指正,留言分享交流~