Go语言中常见100问题-#1 Unintended variable shadowing
小心变量遮蔽
变量的作用域是指一个变量可以被引用的地方/范围。换句话说,就是应用程序的一个区域,在该区域内变量是有效的,超出该区域便无效。在Go语言中,在代码块中声明的变量可以在内部代码块中重新声明,这种使用方法称为变量遮蔽/隐藏,注意在使用时要非常小心,否则很容易出现常见的错误。下面通过一个具体的程序例子说明变量遮蔽/隐藏导致的问题,程序中将以两种不同的方式创建 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, 然后在两个内部代码块中使用短变量声明运算符(:=)赋值,虽然赋值给的变量也是client,但是它与外面的client不是同一个,因此,执行上述程序外部的client始终为nil.
「NOTE: 上面的代码可以编译通过,因为内部赋值的client变量在log.Println中使用到了,否则的话,将出现编译错误,提示client(内部的)声明但未使用。」
如何修复上面代码中存在的问题呢?有两种不同的方法。方法一是在内部代码块中使用一个临时变量保存client,此临时变量名不要使用client,然后再将临时变量值赋值给client, 实现代码如下。这里先将结果保存在临时变量c中,c的作用域在if块中,最后再将c赋值给客户端变量client.
var client *http.Client
if tracing {
c, err := createClientWithTracing()
if err != nil {
return err
}
client = c
} else {
// Same logic
}
方法二是使用赋值运算符(=)将创建结果直接分配给客户端变量client,但是需要创建一个错误变量,因为赋值运算符(=)对已声明的变量才能使用。然后直接将创建结果分配给client,实现如下。
var client *http.Client
var err error
if tracing {
client, err = createClientWithTracing()
if err != nil {
return err
}
} else {
// Same logic
}
上述两种方法都是正确的,主要区别在于方法二种只执行了一个赋值操作,阅读起来可能更容易。此外,使用方法二,可以在if/else语句之后统一对错误进行处理。
if tracing {
client, err = createClientWithTracing()
} else {
client, err = createDefaultClient()
}
if err != nil {
// Common error handling
}
总结,在内部代码块中重新声明变量时,会产生变量遮蔽/隐藏,通过前面的例子可以看到这种做法很容易出错。所以在编码中,注重代码品味,尽量不要犯变量遮蔽/隐藏问题。虽然有时重用现有变量会非常方便,例如在用err表示错误时。但是,总体来说,我们应该谨慎小心,否则很容易出现问题,像本文举的例子,接收到值的变量不是我们预期的变量。
相关文章
- 8天学通MongoDB——第八天 驱动实践
- 8天学通MongoDB——第七天 运维技术
- 8天学通MongoDB——第六天 分片技术
- 8天学通MongoDB——第五天 主从复制
- 8天学通MongoDB——第四天 索引操作
- 8天学通MongoDB——第三天 细说高级操作
- 8天学通MongoDB——第二天 细说增删查改
- 8天学通MongoDB——第一天 基础入门
- Linux命令对应的英文及整体学习法
- 在线客服系统源码开发实战总结:Golang的Gin框架整合实现session
- Golang实现小型CMS内容管理功能(二):前端接入百度ueditor富文本编辑器
- 在线客服系统源码开发实战总结:Golang实现CMS内容管理增删查改功能
- 在线客服系统源码开发实战总结:Golang实现对接微信公众号网页授权接口功能
- 好用的在线客服系统Go语言源码-GOFLY ( 开源代码+安装教程)
- 解决golang报错:imports github.com/go-sql-driver/mysql from implicitly required module;
- golang连接MySQL并导入执行SQL文件
- 解决golang panic: sql: unknown driver “mysql“ (forgotten import?)
- golang封装http get函数请求并且携带header头信息
- golang实现RSA2的签名与验签函数
- Linux中tac命令倒序查询日志