go语言常见陷阱

本文介绍了Go语言中常见的陷阱,包括使用`range`遍历切片时不能直接修改元素,需要通过指针操作;Go语言中三个相连的点`...`与C语言中的可变参数不同,它会被编译为切片;以及Go语言切片的复制,直接使用切片创建的副本与原切片共享底层数组,需要通过`append`或`make+copy`来创建独立副本。
摘要由CSDN通过智能技术生成

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值