【Go 專家編程】內置方法 append 陷阱

Golang 內置方法append用於向切片中追加一個或多個元素,實際項目中比較常見。git

其原型以下:github

func append(slice []Type, elems ...Type) []Type

本節不會對append的使用方式詳細展開,而是重點介紹幾個使用中常見的誤區或者陷阱。golang

熱身

按照慣例,咱們先拿幾個小題目來檢測一下對append的理解是否足夠深入。數組

題目一

函數Validation()用於一些合法性檢查,每遇到一個錯誤,就生成一個新的error並追加到切片errs中, 最後返回包含全部錯誤信息的切片。 爲了簡單起見,假定函數發現了三個錯誤,以下所示:app

func Validation() []error {
	var errs []error

	append(errs, errors.New("error 1"))
	append(errs, errors.New("error 2"))
	append(errs, errors.New("error 3"))

	return errs
}

請問函數Validation()有什麼問題?函數

題目二

函數ValidateName()用於檢查某個名字是否合法,若是不爲空則認爲合法,不然返回一個error。 相似的,還能夠有不少檢查項,好比檢查性別、年齡等,咱們統稱爲子檢查項。 函數Validations()用於收集全部子檢查項的錯誤信息,將錯誤信息彙總到一個切片中返回。工具

請問函數Validations()有什麼問題?code

func ValidateName(name string) error {
	if name != "" {
		return nil
	}

	return errors.New("empty name")
}

func Validations(name string) []error {
	var errs []error

	errs = append(errs, ValidateName(name))

	return errs
}

陷阱

前面的熱身題目均來源於實際項目(已經作了最大程度的精簡),分別表明一個本節將要介紹的陷阱。get

陷阱一: append 會改變切片的地址

append的本質是身切片中追加數據,而隨着切片中元素逐漸增長,當切片底層的數組將滿時,切片會發生擴容, 擴容會致使產生一個新的切片(擁有容量更大的底層數組),更多關於切片的信息,請查閱切片相關章節。原型

append每一個追加元素,都有可能觸發切片擴容,也即有可能返回一個新的切片,這也是append函數聲明中返回值爲切片的緣由。實際使用中應該老是接收該返回值。

上述題目一中,因爲初始切片長度爲0,因此實際上每次append都會產生一個新的切片並迅速拋棄(被gc回收)。 原始切片並無任何改變。須要特別說明的是,無論初始切片長度爲多少,不接收append返回都是有極大風險的。

另外,目前有不少的工具能夠自動檢查出相似的問題,好比GolandIDE就會給出很明顯的提示。

陷阱二: append 能夠追加nil值

向切片中追加一個nil值是徹底不會報錯的,以下代碼所示:

slice := append(slice, nil)

通過追加後,slice的長度遞增1。

實際上nil是一個預約義的值,即空值,因此徹底有理由向切片中追加。

上述題目二中,就是典型的向切片中追加nil(當名字爲空時)的問題。單純從技術上講是沒有問題,但在題目二場景中就有很大的問題。

題目中函數用於收集全部錯誤信息,沒有錯誤就不該該追加到切片中。因後,後續極有可能會跟據切片的長度來判斷是否有錯誤發生,好比:

func foo() {
	errs := Validations("")
	
	if len(errs) > 0 {
		println(errs)
		os.Exit(1)
	}
}

若是向切片中追加一個nil元素,那麼切片長度則再也不爲0,程序極可能所以而退出,更糟糕的是,這樣的切片是沒有內容會打印出來的,這無疑又增長了定位難度。

贈人玫瑰手留餘香,若是以爲不錯請給個贊~ 你的鼓勵將成爲我繼續寫做的動力!

本篇文章已歸檔到GitHub項目,求星~ 點我即達

相關文章
相關標籤/搜索