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
目录
相关文章
|
1天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
15 7
|
4天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
18 2
|
5天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
5天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
14 3
|
5天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
16 3
|
7天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
6天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
21 3
|
10天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
34 4
|
10天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
45 1
|
8天前
|
存储 Go PHP
Go语言中的加解密利器:go-crypto库全解析
在软件开发中,数据安全和隐私保护至关重要。`go-crypto` 是一个专为 Golang 设计的加密解密工具库,支持 AES 和 RSA 等加密算法,帮助开发者轻松实现数据的加密和解密,保障数据传输和存储的安全性。本文将详细介绍 `go-crypto` 的安装、特性及应用实例。
28 0