go语言常见陷阱
(英文原文)[https://deadbeef.me/2018/01/go-gotchas]
Range
在golang中我们经常用range来遍历slice或chan,如果要更改slice中的成员应该怎么做?下面代码要把动物园中所有动物的腿变为999
type Animal struct {
name string
legs int
}
func main() {
zoo := []Animal{ Animal{ "Dog", 4 },Animal{ "Chicken", 2 },Animal{ "Snail", 0 },}
fmt.Printf("-> Before update %v\n",zoo)
for _,animal := range zoo {
// Oppps! `animal` is a copy of an element
animal.legs = 999
}
fmt.Printf("\n-> After update %v\n",zoo)
}
打印结果如下:
-> Before update [{Dog 4} {Chicken 2} {Snail 0}]
-> After update [{Dog 4} {Chicken 2} {Snail 0}]
原因在于range遍历slice是,value是slice中值的拷贝,所有无法真正修改slice。
解决方法
for idx,_ := range zoo {
zoo[idx].legs = 999
}
三个相连的…
在c语言中,我们使用…表示可变参数,用法如下
int add_em_up (int count,...) {
...
va_start (ap,count); /* Initialize the argument list */
for (i = 0; i < count; i++)
sum += va_arg(ap,int); /* Get the next argument value */
va_end (ap); /* Clean up */
return sum
}
在golang中有所不同
func myFprint(format string,a ...interface{}) {
if len(a) == 0 {
fmt.Printf(format)
} else {
// ⚠️ `a` should be `a...`
fmt.Printf(format,a)
// ✅
fmt.Printf(format,a...)
}
}
func main() {
myFprint("%s : line %d\n","file.txt", 49)
}
打印结果
[file.txt %!s(int=49)] : line %!d(MISSING)
file.txt : line 49
在go中,可变参数被编译器转换为slice
所以对于上面的例子,要打印可变参数也可以用下面的方法:
// `a` is just a slice!
for _,elem := range a {
fmt.Println(elem)
}
Slice
在python中对一个列表使用slice,结果会产生一个新的列表
a = [1,2,3]
b = a[:2] # 产生一个新的list
b[0] = 999
>>> a
[1,3]
>>> b
[999,2]
但是在golang中,slice其实只是指向原slice的一个指针,对原slice或新的slice修改都会对双方产生同样修改。
func main() {
data := []int{1,2,3}
slice := data[:2]
slice[0] = 999
fmt.Println(data)
fmt.Println(slice)
}
打印结果
[999 2 3]
[999 2]
在golang中如何根据已有的slice创建一个copy
// Option #1
// appending elements to a nil slice
// `...` changes slice to arguments for the variadic function `append`
a := append([]int{},data[:2]...)
// Option #1
// Create slice with length of 2
// copy(dest,src)
a := make([]int, 2)
copy(a,data[:2]
根据StackOverflow,append要比make+copy更快一些。
原文链接:https://www.f2er.com/go/187510.html