What does this print? (Shadowed err)
Question 318MediumGo 1.22 to 1.25
func parse(s string) error {
var err error
if s != "" {
n, err := strconv.Atoi(s) // := declares a NEW err in this block
if err == nil {
fmt.Println("parsed", n)
}
}
return err
}
func main() {
fmt.Println(parse("abc")) // <nil>
}
It prints <nil>. Inside the if block, := declares new variables n and err that shadow the outer err. The Atoi failure goes to the inner variable and is lost when the block ends, so the function reports success. The shadow analyzer (install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow, then run go vet -vettool=$(which shadow)) and golangci-lint's govet shadow check flag this.
A related gotcha is a bare return with named results. If a named result is shadowed in the scope of the return, it's a compile error: err is shadowed during return.
func f() (err error) {
if _, err := os.Open("x"); err != nil {
return // compile error: err is shadowed during return
}
return
}
Fix: declare n first and use = (var n int; n, err = strconv.Atoi(s)), or return from inside the block.
More on Error Handling & panics
- Q316"Handle an error only once." What does that mean, and what is wrong with log-and-return?
- Q317Why is
defer f.Close()potentially a bug when writing files? How do you handle theCloseerror? - Q319What does
fmt.Errorf("op: %w", err)return whenerris nil? Why does this matter in helper functions? - Q320Explain context errors:
context.Canceled,DeadlineExceeded, andcontext.Cause. - Q321Why is
errors.Is(err, fs.ErrNotExist)preferred overos.IsNotExist(err)? - Q322What is "asserting errors for behavior"? How would you implement retry logic based on error behavior?