Go变量遮蔽导致的意外错误

阳了快一个星期了,没有更新文章。也在思考发些什么内容比较实用。之前发的一些文章都是比较基础的适合普通的Go开发人员交流学习。之前看到国外有人在整理Go的常见错误,这个也是每个编程人都希望掌握的,毕竟谁都不想犯错。
现在网上也可以阅读原版内容了,这里我也把相关的一些内容翻译下,供大家参考。


今天要介绍的第一个易犯错误就是,变量作用域导致的。

变量的作用域是指变量可以被引用的范围。换句话说,是应用程序中变量名绑定有效的部分。在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

1、申明一个client变量
2、如果tracing是true创建HTTP客户端。在这里第一步定义的client变量被遮蔽。
3、创建一个默认http客户端。这里也会发生变量的遮蔽。
在这个例子中,我们首先申明client变量。然后在内部代码块if...else..中使用短变量申明操作符(:=)将函数返回值赋给内部变量client,而不是外部client变量。因此,外部变量client还是初始值nil。

注意:这段代码能编译通过,是因为内部client变量在log.Println中使用了。否则,编译会报错例如client declared and not used.

我们该如何确保最初的client变量被正确赋值呢?

第一种方法:在内部代码块中使用临时变量,如下所示:

var client *http.Client
if tracing {
    c, err := createClientWithTracing()    ❶
    if err != nil {
        return err
    }
    client = c                                           ❷
} else {
    // Same logic
}

1、创建临时变量c。
2、将临时变量值赋给client。
这里将函数调用结果赋值给脸是变量c,它的作用域只在if...else...代码块范围内。然后将值赋给client。同时对else部分做相同处理。

第二种方法:在内层代码块里面使用赋值操作符(=),将函数返回值直接赋给client变量。但是,这里需要定义error变量,因为赋值操作符仅作用于已经定义的变量。例如:

var client *http.Client
var err error                                                ❶
if tracing {
    client, err = createClientWithTracing()    ❷
    if err != nil {
        return err
    }
} else {
    // Same logic
}

1、申明err变量。
2、使用赋值操作符将返回的*http.Client值直接赋给client变量。
这里取代传值给临时变量,而是直接将结果传给client。

以上两种方式都有效。主要区别在第二种方法只需要一次赋值,使代码便于阅读。同时,使用第二种方式,我们可以在if/else语句之外实现错误处理。

if tracing {
    client, err = createClientWithTracing()
} else {
    client, err = createDefaultClient()
}
if err != nil {
    // Common error handling
}

当在内部代码块中重新声明变量名时,就会发生变量遮蔽,但我们看到这种做法容易出错。禁止变量遮蔽的规则取决于个人喜好。例如,有时重用现有的变量名(如err用于错误)会很方便。然而,一般来说,我们应该保持谨慎,因为我们现在知道可能会面临这样的场景:代码可编译完成,但接收值的变量不是预期的值。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 面试中的C++常见问题 1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”? 答:首...
    程序爱好者阅读 463评论 0 0
  • 目录 统一规范篇 命名篇 开发篇 优化篇 统一规范篇 本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开...
    零一间阅读 2,142评论 0 2
  • JavaScript,通常缩写为 JS,是一种解释执行的编程语言。它是现在最流行的脚本语言之一。 JavaScri...
    神齐阅读 5,411评论 1 32
  • 1、选择合适的算法和数据结构 3 2、使用尽量小的数据类型 3 3、减少运算的强度 3 (1)查表 3 (2)求余...
    郑行_aover阅读 346评论 0 0
  • 简介 ECMAScript是JavaScript的标准,JavaScript实现了ECMAScript,ECMAS...
    Zindex阅读 571评论 0 1

友情链接更多精彩内容