1. 程式人生 > >go新手容易犯的幾個錯誤

go新手容易犯的幾個錯誤

前言

最近寫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到新切片中!!