【Go常见错误】2. 意外的变量隐藏

概念

在Go中,什么叫变量隐藏呢?

在一个代码块中声明的变量名称可以在其内部块中重新声明,这被称作变量隐藏

然而这种规则很容易出现错误。

错误案例

在下面的例子中,将会看到一个关于变量隐藏而产生的bug。

var client *http.Client //①
if tracing {
  client, err := createClientWithTracing() //②
  if err != nil {
    return err
  }
  log.Println(client)
} else {
  client, err := createDefaultClient() //③
  if err != nil {
    return err
  }
  log.Println(client)
}

//use client
...

① 声明一个client变量

② 创建一个带tracing的HTTP客户端,client变量在该块内被隐藏了

③ 创建一个默认的HTTP客户端,client变量在该模块依然被隐藏掉了。

首先,我们声明了一个client变量。然后,在两个内部块中,我们使用 := 操作符,也叫做短变量声明运算符。该操作符使用和开始的时候相同的名称创建了一个新的client变量;它不会为第①行中的client变量赋值。

因此,在该示例中,在① 声明的HTTP客户端变量client将始终是nil值

解决方法

我们如何确保给client赋值了呢?有两种不同的方法。

1. 在内部块中使用临时变量

像下面这样:

var client *http.Client
if tracing {
    c, err := createClientWithTracing() //①
    if err != nil {
    return err
    }
    client = c //②
} else {
    c, err := createDefaultClient()
    if err != nil {
        return err
    }
    client = c
}

// Use client

① 创建了一个临时变量c

② 将临时变量赋给变量client。

变量c的生命周期只在if/else块中。然后,我们将这些变量赋值给client。

2. 内部块中继续使用外部定义变量

比如针对这个例子,在内部块中client变量采用赋值操作(=),而不是初始化并赋值操作(:=)

然而,它需要创建一个error变量,因为赋值运算符仅在已声明变量时才起作用。

var client *http.Client
var err error //①
if tracing {
  client, err = createClientWithTracing() //②
  if err != nil {
    return err
  }
} else {
  client, err = createDefaultClient()
  if err != nil {
    return err
  }
}

① 声明变量err

② 使用赋值操作符将返回来的*http.Client直接赋值给client变量

在这个例子中,我们也将内部调用的结果赋值给了client。

哪种方法最好呢?第一种方法在大多数情况下都是更方便的,但是没有强迫说要是用哪种方法。

当在内部块中将一个变量名重新声明时就会发生变量隐藏。

我们已经看到这种做法很容易出错,应根据项目和上下文制定避免隐藏变量的规则。

避免方法

首先还是建议大家在编码时,尽量避免这个规则,定义重复变量时仔细检查一下作用域。

那我们有没有办法自动化检查呢?使用vet和shadow工具

package main

import "fmt"

func main() {
    i := 0
    if true {
        i := 1 //隐藏的变量
    }
    fmt.Println(i)
}
$ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow 
$ go vet -vettool=$(which shadow) 
./main.go:8:3: declaration of "i" shadows declaration at line 6

① shadow安装

② 使用vettol参数将shadow链接到vet

③ Go vet可以检测隐藏的变量了

注:在执行go vet -vettool=$(which shadow) 时如果which shadow找不到路径,可以将其换成shadow的绝对路径。使用go install安装shadow命令默认安装在GOPATH下的bin目录中:GOPATH/bin/shadow。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值