本文是对 《100 Go Mistackes:How to Avoid Them》 一书的翻译。因翻译程度无限,不免存在翻译准确性问题,敬请体谅

变量的作用域是指它的可见性。换句话说,程序中的名称在哪局部是无效的。在Go中,在块中申明的变量名称能够在外部块中从新申明。这种被称为变量暗藏的准则很容易呈现谬误。

在上面的例子中,咱们将看到一个对于变量暗藏产生的bug。咱们将应用两种不同的形式创立一个HTTP客户端,具体取决于tracing布尔值:

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客户端将始终是nil值。

留神:该代码之所以能够编译胜利,是因为在logging调用中应用了外部变量client。否则,咱们就会有编译谬误:client declared and not used。

咱们如何确保给client赋值了呢?有两种不同的办法。
第一种办法是在外部块中应用长期变量,像上面这样:

var client *http.Clientif 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。

第二种形式是在外部块中应用赋值操作符(=)来将函数的返回值间接赋值给client变量。然而,它须要创立一个error变量,因为赋值运算符仅在已申明变量时才起作用。

var client *http.Clientvar 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。哪种办法最好呢?第一种办法在大多数状况下都是更不便的,然而没有强制说要是用哪种办法。

当在外部块中将一个变量名从新申明时就会产生变量暗藏,咱们曾经看到这种做法很容易出错。应依据我的项目和上下文制订防止暗藏变量的规定。例如,有时候,重用现有的变量名可能会很不便,像err谬误。然而,一般来说,咱们应该放弃审慎,因为咱们曾经看到咱们可能会面临这样一种谬误:代码能够编译,但可能不会对咱们冀望的变量进行赋值。在本章前面,咱们将看到该如何检测变量暗藏,以帮忙咱们发现可能的谬误。