Go 語言怎么解決編譯器錯誤“err is shadowed during return”?
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