前陣子在網上看到一些關於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建立的對象返回的都是對應的值類型。app
1. new返回的是T的指針,make返回的是T的值。編程語言
2. make僅能用於建立slice,map,channel。函數
對於變量命名,Go大師Dave Cheney舉了個頗有趣比喻:你給變量命名就像給你家的寵物取名同樣,名字上不要帶上「xx狗」,」xx貓「,由於你們都能知道它是狗仍是貓。ui
因此你的變量名應該是描述變量的內容,而不是描述變量的類型,看看如下的寫法。spa
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/…
感謝閱讀,歡迎你們指正,留言分享交流~