Go語言開發小技巧&易錯點100例(二)

語言: CN / TW / HK

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第7天,點擊查看活動詳情

往期回顧:

本期看點(技巧類用【技】表示,易錯點用【易】表示)

(1)Go Module中對依賴庫版本的升級與降級【技】

(2)Go goroutine中的異常處理【易】

(3)Go中slice作為參數是值傳遞【技】

正文

1 Go Module中對依賴庫版本的升級與降級【技】

Go Module引用其他的依賴庫分為兩種方式:

  • 引用該庫的某一個tag
  • 引用該庫的某一個分支(稱之為偽版本)

因而顯示上也會有差別(大多數情況下),如下:

shell github.com/coreos/go-semver v0.3.0 //引用tag github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf //引用分支

但是不管是引用tag還是引用分支,指向的都是該倉庫的分支或tag提交最後一個commit時的鏡像,因而版本的升級與降級的最小顆粒度就是commit

先看針對tag進行升降級

```shell

查看該依賴庫的所有版本

go list -m -versions github.com/xuri/excelize/v2

輸出:

github.com/xuri/excelize/v2 v2.0.0 v2.0.1 v2.0.2 v2.1.0 v2.2.0 v2.3.0 v2.3.1 v2.3.2 v2.4.0 v2.4.1 v2.5.0 v2.6.0 v2.6.1

查看當前項目依賴庫的版本

go list -m github.com/xuri/excelize/v2

輸出:

github.com/xuri/excelize/v2 v2.6.1

指定想要切換到的版本

go get github.com/xuri/excelize/[email protected]

輸出:

go: downloading github.com/xuri/excelize/v2 v2.6.0 go get: downgraded github.com/xuri/excelize/v2 v2.6.1 => v2.6.0 ```

針對分支或commit升降級(前提是知道之前一個commit的偽版本信息)

比如你之前一個引用的commit是

shell github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf

目前是:

shell github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf

想要切換回去,可能go get就不是很管用了,那麼就需要使用新的方式:

shell replace github.com/coreos/go-systemd => github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf

即可將依賴的庫指定到想要的commit

PS:偽版本的概念

這種是因為依賴模塊的代碼倉庫上不存在任何tag,所以go get 默認拉取的是master分支最新一次commit對應版本的代碼,並且在go.mod文件裏為模塊分配格式為

shell v0.0.0-[主幹分支最新一次commit的時間]-[commit哈希值]

2 Go goroutine中的異常處理【易】

首先看一下這種情況:

go func main() { waitGroup := sync.WaitGroup{} waitGroup.Add(1) go func() { defer waitGroup.Done() fmt.Println("Hello goroutine ...") panic("err") }() waitGroup.Wait() fmt.Println("Hello main ...") }

運行結果大家猜能不能打印出Hello main ...?

答案:不能

問題產生的原因就是在goroutine中我們沒有進行對可能發生的異常進行處理,當goroutine中發生panic的時候會影響主函數的運行,那麼我們應該如何處理呢?

使用recover處理異常

go func main() { waitGroup := sync.WaitGroup{} waitGroup.Add(1) go func() { defer func() { if e := recover(); e != nil { fmt.Println("recover panic") } waitGroup.Done() }() fmt.Println("Hello goroutine ...") panic("err") }() waitGroup.Wait() fmt.Println("Hello main ...") }

優化點:

  • 注意打印異常日誌
  • 使用channel將錯誤信息返回至主線程處理

3 Go中slice作為參數是值傳遞【技】

我們直接來試一下:

```go func Test(list []string) { fmt.Println(list) fmt.Println(unsafe.Pointer(&list)) }

func main() { list := make([]string, 0) list = append(list, "a", "b", "c") fmt.Println(list) fmt.Println(unsafe.Pointer(&list)) Test(list) } ```

輸出結果:

shell [a b c] 0xc000004078 [a b c] 0xc0000040a8

今天的分享就先到這裏咯~