go语言常见陷阱

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, 2, 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更快一些。

相关文章
相关标签/搜索