前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 语言怎么解决编译器错误“err is shadowed during return”?

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

作者头像
frank.
发布2022-10-28 10:32:50
6600
发布2022-10-28 10:32:50
举报
文章被收录于专栏:Golang语言开发栈

01

介绍

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

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

02

为什么出现该错误?

示例代码:

代码语言:javascript
复制
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
}

输出结果:

代码语言:javascript
复制
./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”。

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

示例代码:

代码语言:javascript
复制
...
func foo() (err error) {
 if err1 := bar(); err1 != nil {
  return
 }
 return nil
}
...

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

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

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

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

代码语言:javascript
复制
...
func foo() (err error) {
 if err1 := bar(); err1 != nil {
    err = err1
  return
 }
 return nil
}
...

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

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

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

代码语言:javascript
复制
func foo() (err error) {
 if err := bar(); err != nil {
  return err
 }
 return nil
}

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

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

代码语言:javascript
复制
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
}

输出结果:

代码语言:javascript
复制
./main.go:19:3: err is shadowed during return

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

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

代码语言:javascript
复制
...
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. Go 语言使用 goroutine 运行闭包的“坑”
  2. Go 语言内存逃逸案例
  3. Golang 语言的多种变量声明方式和使用场景
  4. Golang 语言 vendor 在 GOPATH 和 Modules 中的区别
  5. Golang 语言中的非类型安全指针

参考资料:

  1. https://groups.google.com/g/golang-nuts/c/HmmZXC7KcVw?pli=1
  2. https://go.dev/ref/spec#Short_variable_declarations
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档