本文是对《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.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。
第二种形式是在外部块中应用赋值操作符(=)来将函数的返回值间接赋值给 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。哪种办法最好呢?第一种办法在大多数状况下都是更不便的,然而没有强制说要是用哪种办法。
当在外部块中将一个变量名从新申明时就会产生 变量暗藏,咱们曾经看到这种做法很容易出错。应依据我的项目和上下文制订防止暗藏变量的规定。例如,有时候,重用现有的变量名可能会很不便,像 err 谬误。然而,一般来说,咱们应该放弃审慎,因为咱们曾经看到咱们可能会面临这样一种谬误:代码能够编译,但可能不会对咱们冀望的变量进行赋值。在本章前面,咱们将看到该如何检测变量暗藏,以帮忙咱们发现可能的谬误。