In our daily work, we often useerr != nil
To determine whether the program or function reports an error, or usedefer {recover = err}
To determine whether there ispanic
Serious error, but if you don’t pay attention, it’s easy to fall intoerr shadow
A trap.
1. Variable scope
package main
import "fmt"
func main() {
x := 100
func() {
x := 200 // x will shadow outer x
fmt.Println(x)
}()
fmt.Println(x)
}
The output is as follows:
200
100
Result analysis:x
Variable infunc
Printed as200
, print as100
, this is the scope of the variable(variable scope
)。func
Variables insidex
Is a new variable, just with the outer layerx
Duplicate name(variable redeclaration
)At this time, the inner layerx
The scope of is limited tofunc {} block
And the outer layerx
The scope of ismain {} block
At this time, the inner layer variablex
It happenedvariable shadowing
, outer layerx
Not affected, still100
。
Change the wording:
package main
import "fmt"
func main() {
x := 100
func() {
x = 200 // x will override outer x
fmt.Println(x)
}()
fmt.Println(x)
}
The output is as follows:
200
200
At this point,func
Variables insidex
It just covers the outer layerx
, no new variables are defined, so both inner and outer outputs are200
。
2. Err shadow – unknown error
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("func err1:", test1())
}
func test1() error {
var err error
defer func() {
fmt.Println("defer err1:", err)
}()
if _, err := os.Open("xxx"); err != nil {
return err
}
return nil
}
The output is as follows:
defer err1: <nil>
func err1: open xxx: no such file or directory
Result analysis:func test1
First definedvar err error
Variable, but the followingos.Open
Error reporting useerr :=
Be localerr shadow
Yes, although it is explicitly usedreturn err
An error was returned, but becausetest1() error
The return parameter is anonymous(unnamed variable
), resulting indefer
inerr
Failed to geterr shadow
Error oferr
, the outer initialization is still takenvar err error
Value, so the output iserr1: <nil>
。
You just need to19
If you change the line, you can avoid iterr shadow
:
if _, err = os.Open("xxx"); err != nil {
return err
}
The output is as follows:
defer err1: open xxx: no such file or directory
func err1: open xxx: no such file or directory
3. Err shadow – name error
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("func err2:", test2())
}
func test2() (err error) {
defer func() {
fmt.Println("defer err2:", err)
}()
if _, err := os.Open("xxx"); err != nil {
return // return without err will compilation error
}
return
}
abovetest2
There will be compilation errors during operation, which isgo compiler
Done at compile timevariable shadowing
Check and directly compile and report errors if any. Just modify it:
func main() {
fmt.Println("func err3:", test3())
}
func test3() (err error) {
defer func() {
fmt.Println("defer err3:", err)
}()
if _, err := os.Open("xxx"); err != nil {
return err
}
return
}
The output is as follows:
defer err3: open xxx: no such file or directory
func err3: open xxx: no such file or directory
4. Nested err shadow
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
fmt.Println("func err4:", test4())
}
func test4() (err error) {
defer func() {
fmt.Println("defer err4:", err)
}()
if _, err := os.Open("xxx"); err == nil {
if err := json.Unmarshal([]byte("{}"), &struct{}{}); err == nil {
fmt.Println("OK")
}
}
return
}
The output is as follows:
defer err4: <nil>
func err4: <nil>
Result analysis:func test4()
Is a named returnerr error
, the function initializesvar err error
Define the corresponding named variable(named variable
), but the followingos.Open
orjson.Unmarshal
All usederr :=
redefinitionerr
Variable, resulting inerr shadow
Therefore, when the function exits, the outer layererr
Stillnil
,defer
Acquisition, that isnil
。
Just change the wording:
func main() {
fmt.Println("func err5:", test5())
}
func test5() (err error) {
defer func() {
fmt.Println("defer err5:", err)
}()
if _, err = os.Open("xxx"); err == nil {
if err = json.Unmarshal([]byte("{}"), &struct{}{}); err == nil {
fmt.Println("OK")
}
}
return
}
The output is as follows:
defer err5: open xxx: no such file or directory
func err5: open xxx: no such file or directory
5. Summary
Through several examples, this paper analyzes the problems that are easy to appear in practical workerr shadow
The essential reason for the problem is mainly caused by the variable scope. It is mentioned in the official document:An identifier declared in a block may be redeclared in an inner block. While the identifier of the inner declaration is in scope, it denotes the entity declared by the inner declaration
.
In addition, in the naming of function return values, we need to consider the nameless and famous parameters. It is recommended to use tools when the code logic is correctgo linter
orgo vet
To detect what the compiler didn’t detectvariable shadowing
, avoid stepping on the pit.