zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

2023-02-25 17:58:11 时间

1介绍

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

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

2.为什么出现该错误?

示例代码:

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”。

3.怎么解决?

阅读完 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 也被重新声明。

4.总结

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

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

参考资料:

https://groups.google.com/g/golang-nuts/c/HmmZXC7KcVw?pli=1

https://go.dev/ref/spec#Short_variable_declarations