Explain variable shadowing. Find the bug in this code.
Question 6MediumGo 1.22 to 1.25
var db *sql.DB
func initDB() error {
db, err := sql.Open("postgres", dsn) // BUG: declares a new local db
if err != nil {
return err
}
return db.Ping()
}
// Later: db.Query(...) panics with a nil pointer dereference
:= declares new variables in the current scope. Because db is not already declared in the function scope, := creates a local db that hides the package-level one. The package-level db stays nil. The fix is to declare err separately and use plain assignment:
func initDB() error {
var err error
db, err = sql.Open("postgres", dsn)
if err != nil {
return err
}
return db.Ping()
}
Tricky variant:
x := 1
if x := 2; x > 1 {
fmt.Println(x) // 2
}
fmt.Println(x) // 1
The init statement of if, for and switch opens its own block, so the inner x exists only there. Tools that catch shadowing: the shadow analyzer, which is not part of default go vet (install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow and run go vet -vettool=$(which shadow)), or golangci-lint with the govet linter's shadow check enabled.
More on Language Fundamentals & Types
- Q4What are untyped constants? Explain why
const huge = 1 << 100compiles. - Q5What is the difference between a typed and an untyped constant when you assign or compare it?
- Q7What are the rules for
:=redeclaration, and why does this not compile? - Q8What does this print? (Assignment evaluation order)
- Q9What is the difference between a type conversion and a type assertion?
- Q10In a type switch, what is the type of the bound variable when a case lists several types, and how does
case nilbehave?