【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项目,求星~ 点我即达

相关文章
相关标签/搜索