
2.1#1:意想不到的变量隐藏
变量的作用域指的是变量可以被引用的位置,换句话说,就是应用程序中变量在程序的哪个部分有效。在Go 中,在一个块中声明的变量名可以在其内部块中被重新声明,这个规则被称为变量隐藏,它容易导致出现常见错误。
下面的示例显示了由于变量隐藏而产生的意外的副作用。下面的例子以两种不同的方式创建HTTP客户端,具体取决于布尔值tracing:


在本例中,我们首先声明一个client变量。然后,在两个内部块中使用短变量声明操作符(:=),将函数调用的结果分配给内部client变量而不是外部变量。因此,外部变量总是为nil。
注意 此段代码可编译,因为日志调用中使用了内部client变量。如果没有使用client,就会出现编译错误,比如声明了client端而没有使用。
如何确保赋值给原始client变量?有两种不同的选择。
第一种选择是在内部块中使用临时变量:

在这里,将结果分配给一个临时变量 c,它的作用域仅在if 块内。然后,将它赋值给client变量。同时,我们对else部分做同样的操作。
第二种选择是在内部块中使用赋值操作符(=),直接将函数结果赋值给client变量。但是,这需要创建一个error 变量,因为赋值操作符只有在已经声明了变量名的情况下才有效。例如:

可以直接将结果分配给client,而不是先分配给临时变量。
这两种选择都是完全有效的。这两种选择的主要区别是,在第二种选择中只执行了一次赋值,这可能被认为更容易阅读。另外,通过第二种选择,可以在if/else 语句之外实现错误处理,如下例所示:

当在内部块中重新声明变量名时,就会发生变量隐藏,但可以看到这种做法非常容易引起错误。强制禁止变量隐藏的规则取决于个人喜好。例如,有时可以方便地重用现有的变量名,如 err 用于错误。但是,一般来说,应该保持谨慎,因为我们可能会遇到这样的情况:代码被编译了,但接收值的变量不是预期的那个。在本章的后面,我们还将看到如何检测变量隐藏,这可以帮助我们发现可能的错误。
下一节将展示避免滥用嵌套代码的重要性。