Go 语言怎么解决编译器错误“err is shadowed during return”?

简介: Go 语言怎么解决编译器错误“err is shadowed during return”?

01

介绍

在 Go 语言开发中,我们可能会遇到“错误在返回时被隐藏”的错误,该错误在 Go 编码时很难发现,在 GoLand 中也只是会变量名高亮提示,只有在编译 Go 项目时,Go 编译器会返回 err is shadowed during return

本文我们介绍为什么会出现该错误,以及我们应该怎么解决?

为什么出现该错误?

示例代码:

package main
import (
 "errors"
 "log"
)
func main() {
 err := foo()
 if err != nil {
  log.Printf("err=%v\n", err)
  return
 }
}
func foo() (err error) {
 if err := bar(); err != nil {
  return // Compiler reports: err is shadowed during return
 }
 return nil
}
func bar() error {
 err := errors.New("this is bar's err")
 return err
}

输出结果:

./main.go:18:3: err is shadowed during return

阅读上面这段代码,我们在编译代码时,编译器返回错误“err is shadowed during return”。

因为函数 func foo() (err error) 的返回值是具名参数,其作用域是函数 foo() 的函数体,在函数体中,if 分支使用短变量声明的方式重新声明变量 err,它的作用域是 if 分支。

if 分支声明的变量 err,它的内存地址与外层变量 err 不是同一个内存地址,而在 if 分支中使用 return 返回的是外层变量 err,所以 if 分支中的变量 err 被外层变量 err 所遮蔽,导致在编译 Go 项目时,Go 编译器返回错误“err is shadowed during return”。

03

怎么解决?

阅读完 Part02,读者朋友们已经了解了错误的原因。实际上,出现该错误,归根结底是我们没有真正掌握 Go 的基础知识。

为什么这么说呢?因为在我们公众号的历史文章中,关于 Go 变量声明、作用域、函数等基础知识都有介绍。

如果读者朋友们彻底掌握这些基础知识,大概率是不会遇到该错误“err is shadowed during return”。

解决该错误也比较简单,错误的原因是变量被遮蔽,我们通过使用不同的变量名,可以轻松规避这个错误。

示例代码:

...
func foo() (err error) {
 if err1 := bar(); err1 != nil {
  return
 }
 return nil
}
...

但是,使用不同的变量名真的解决问题了吗?

我们运行使用不同变量名的代码,确实 Go 编译器没有返回错误,我们可以正常编译 Go 项目。

细心的读者朋友们可能已经发现,该解决方案虽然可以规避 Go 编译器返回错误,但是并没有将错误传递到外层变量 err

所以,我们还需要将新变量 err1 的值赋值给外层变量 err,代码如下:

...
func foo() (err error) {
 if err1 := bar(); err1 != nil {
    err = err1
  return
 }
 return nil
}
...

现在,我们才算彻底解决了问题。改造后的代码,既不会引起 Go 编译器返回错误,也可以将错误信息传递出去。

读者朋友们如果有代码“洁癖”,肯定觉得这么写代码太不优雅了。那么,有没有优雅的解决方案呢?

答案是有更优雅的解决方案,我们在讲变量作用域的文章中也有讲过,在具名返回值的函数中,如果在函数体不同作用域中使用同名变量,不能直接返回,而是需要在 return 后面跟上变量名。

func foo() (err error) {
 if err := bar(); err != nil {
  return err
 }
 return nil
}

阅读上面这段代码,我们在 if 分支的作用域中,在 return 后面跟上变量名 err,该方式也可以解决问题,而且比使用不同变量名的方式更优雅。

现在,我们学会了两种解决方案。但是,还没有结束。我们示例代码中,调用函数 bar 是单返回值,在实际项目开发中,还会遇到调用函数是多返回值。

package main
import (
 "errors"
 "log"
)
func main() {
 err := foo()
 if err != nil {
  log.Printf("err=%v\n", err)
  return
 }
}
func foo() (err error) {
 if code, err := bar(); err != nil {
  log.Printf("code=%v err=%v\n", code, err)
  return // Compiler reports: err is shadowed during return
 }
 return nil
}
func bar() (int, error) {
 err := errors.New("this is bar's err")
 return 200, err
}

输出结果:

./main.go:19:3: err is shadowed during return

阅读上面这段代码,调用函数 bar() 是多返回值。

对于调用函数是多返回值的情况,除了我们已经讲的两种解决方式,还有其它解决方式。

...
func foo() (err error) {
  var code int
 if code, err = bar(); err != nil {
  log.Printf("code=%v err=%v\n", code, err)
  return
 }
 return nil
}
...

阅读上面这段代码,我们单独声明新变量 code,而不是使用短变量的方式声明新变量 code,避免变量 err 也被重新声明。

04

总结

本文我们介绍 Go 语言编译错误 err is shadowed during return 的原因和解决方案。先是介绍出现该错误的原因,然后介绍了解决该错误的三种解决方式。

需要注意的是,我们示例代码 foo 函数是具名返回值,本文讲的解决方案并不适用于匿名返回值的函数。

推荐阅读:

参考资料:

  1. https://groups.google.com/g/golang-nuts/c/HmmZXC7KcVw?pli=1
  2. https://go.dev/ref/spec#Short_variable_declarations
目录
相关文章
|
28天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
77 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
1月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
1月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
115 67
|
9天前
|
算法 安全 Go
Go语言中的加密和解密是如何实现的?
Go语言通过标准库中的`crypto`包提供丰富的加密和解密功能,包括对称加密(如AES)、非对称加密(如RSA、ECDSA)及散列函数(如SHA256)。`encoding/base64`包则用于Base64编码与解码。开发者可根据需求选择合适的算法和密钥,使用这些包进行加密操作。示例代码展示了如何使用`crypto/aes`包实现对称加密。加密和解密操作涉及敏感数据处理,需格外注意安全性。
31 14
|
9天前
|
Go 数据库
Go语言中的包(package)是如何组织的?
在Go语言中,包是代码组织和管理的基本单元,用于集合相关函数、类型和变量,便于复用和维护。包通过目录结构、文件命名、初始化函数(`init`)及导出规则来管理命名空间和依赖关系。合理的包组织能提高代码的可读性、可维护性和可复用性,减少耦合度。例如,`stringutils`包提供字符串处理函数,主程序导入使用这些函数,使代码结构清晰易懂。
49 11
|
9天前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
|
13天前
|
监控 安全 算法
深度剖析核心科技:Go 语言赋能局域网管理监控软件进阶之旅
在局域网管理监控中,跳表作为一种高效的数据结构,能显著提升流量索引和查询效率。基于Go语言的跳表实现,通过随机化索引层生成、插入和搜索功能,在高并发场景下展现卓越性能。跳表将查询时间复杂度优化至O(log n),助力实时监控异常流量,保障网络安全与稳定。示例代码展示了其在实际应用中的精妙之处。
36 9
|
23天前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
58 12
|
26天前
|
监控 算法 安全
解锁企业计算机监控的关键:基于 Go 语言的精准洞察算法
企业计算机监控在数字化浪潮下至关重要,旨在保障信息资产安全与高效运营。利用Go语言的并发编程和系统交互能力,通过进程监控、网络行为分析及应用程序使用记录等手段,实时掌握计算机运行状态。具体实现包括获取进程信息、解析网络数据包、记录应用使用时长等,确保企业信息安全合规,提升工作效率。本文转载自:[VIPShare](https://www.vipshare.com)。
30 1
|
1月前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数