Go语言的三个雷区

下面的例子都是在Kubernetes代码中的,而且据我所知,都不止一次地通过了代码审查。

1. 循环中变量的作用域问题

观察下面的代码,预测其功能是什么?

func print(pi *int) { fmt.Println(*pi) }

for i := 0; i < 10; i++ {
  defer fmt.Println(i)
  defer func(){ fmt.Println(i) }()
  defer func(i int){ fmt.Println(i) }(i)
  defer print(&i)
  go fmt.Println(i)
  go func(){ fmt.Println(i) }()
}

答案:

func print(pi *int) { fmt.Println(*pi) }

for i := 0; i < 10; i++ {
  defer fmt.Println(i) // OK; prints 9 ... 0
  defer func(){ fmt.Println(i) }() // WRONG; prints "10" 10 times
  defer func(i int){ fmt.Println(i) }(i) // OK
  defer print(&i) // WRONG; prints "10" 10 times
  go fmt.Println(i) // OK; prints 0 ... 9 in unpredictable order
  go func(){ fmt.Println(i) }() // WRONG; totally unpredictable.
}

for key, value := range myMap {
  // Same for key & value as i!
}

大家都认为这些变量能正常工作,但实际上,Go语言随着迭代会重用相同的内存。这意味着,你永远不能让key,value,i的值在循环外使用。匿名函数func() { /* do something with i */ }(一个“闭包”)是解决这个问题的巧妙办法, 其实闭包传递的是引用(地址),恰恰是引起这个问题的元凶

2. Nil接口并不是有Nil指针的接口

type Cat interface {
  Meow()
}

type Tabby struct {}
func (*Tabby) Meow() { fmt.Println("meow") }

func GetACat() Cat {
  var myTabby *Tabby = nil
  // Oops, we forgot to set myTabby to a real value
  return myTabby
}

func TestGetACat(t *testing.T) {
  if GetACat() == nil {
    t.Errorf("Forgot to return a real cat!")
  }
}

猜到了吗?上面的代码不会发现Nil指针! 

一个interface,其内部的值包括type和value。只有当两个值都是空{type:nil,value:nil},我们才认为该interface==nil成立。由于赋值的方式,导致myTabby的type不为空{*Tabby,nil},因此该Cat接口指针不为nil,即使value的pointer==nil。

这是因为,接口作为一个指针,所以GetACat实际上将一个指针返回给了空指针。永远不要写上面这种代码,这样你的同事会很高兴的。用错误值来代替更好。(http://golang.org/doc/faq#nil_error

3. 有害的变量名

var ErrDidNotWork = errors.New("did not work")

func DoTheThing(reallyDoIt bool) (err error) {
  if reallyDoIt {
    result, err := tryTheThing()
    if err != nil || result != "it worked" {
      err = ErrDidNotWork
    }
  }
  return err
}

上面的函数永远都会返回一个Nil错误,因为内部的err变量覆盖了函数的作用域变量,使用 var result string,不使用:=可以解决这个问题。

原文The Three Go Landmines.markdown 
作者:lavalamp 翻译:赖信涛 责编:仲培艺

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值