分享3個Go編程的小知識

前陣子在網上看到一些關於Go比較不錯的小知識點,下面總結下分享給你們。面試

new和make的區別

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

總結下new和make的區別:

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可能會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/…

感謝閱讀,歡迎你們指正,留言分享交流~

來源:微信公衆號《Go後端乾貨》

各類Go,後端技術,面試題分享,歡迎關注

相關文章
相關標籤/搜索