go新手容易犯的幾個錯誤
阿新 • • 發佈:2019-02-13
前言
最近寫go遇到幾個坑,做個記錄,以免再犯。
高居榜首:通道未初始化
看下面的程式碼執行的結果是什麼?
package main import ( "fmt" "time" ) type test struct { name string } type Object struct { Ch chan *test } func main() { go test1() time.Sleep(100 * time.Second) } func test1() { o := new(Object) fmt.Println(&o, o.Ch) for { //執行下面這行,結果會是什麼? data,err := <-o.Ch fmt.Println(data,err) select { case data := <-o.Ch: fmt.Println(data) case o.Ch <- &test{}: fmt.Println("lll") } } // o.Ch <- &test{} fmt.Println(<-o.Ch) }
當程式執行到data := <-o.Ch
會發生什麼?
返回err?
panic?
還是其他?
正確答案是無限期永久阻塞!!!
原因是:
由於通道型別是引用型別,所以它的零值就是nil。換句話說,當我們只宣告該型別的變數但沒有用make函式對它進行初始化時,該變數的值就會是nil,對於值為nil的通道,不論它的具體型別是什麼,對它的傳送操作和接收操作都會永久地處於阻塞狀態。它們所屬的 goroutine 中的任何程式碼,都不再會被執行。
因此,建議:
通道一定要進行初始化,並且在定義最可能近的地方初始化。
切片copy問題
看下面程式碼:
// 深度遍歷 func (a *Object) dfs(key string, path []string, adj map[string]*collection.Set) { ok, err := utils.Contains(key, path) if nil == err && ok { return } path = append(path, key) if len(newPath) > 1 { a.Paths = append(a.availablePaths, newPath) } if set, ok := adj[key]; ok { s := set.List() for _, key2 := range s { a.dfs(key2, newPath, adj) } } } func (a *Object) calAvailablePath(adj map[string]*collection.Set) { for key := range adj { a.dfs(key, []string{}, adj) } }
上面程式碼實現的是深度遍歷的程式碼,但最後輸出的結果是有重複結果的。
為什麼?
原因出在path = append(path, key)
這一行。應該修改為:
//path = append(path, key)
newPath := make([]string, len(path)+1)
copy(newPath, append(path, key))
原因是每次將path append 時,是保持的引用,後續再append會修改原來的切片。
因此,需要重新make一個切片,將元素copy到新切片中!!