Last modified: Jan 23, 2026 By Alexander Williams
Go: Avoid Variable Shadowing Mistakes
Variable shadowing is a subtle bug in Go. It happens when you declare a new variable with the same name as an existing one in an outer scope. The inner variable "shadows" the outer one. This can lead to confusing logic errors that are hard to spot.
This guide will help you understand, identify, and prevent variable shadowing. You will write cleaner and more reliable Go code.
What is Variable Shadowing?
In Go, variables have scope. A variable declared inside a block (like a function, loop, or if statement) is only accessible within that block and any inner blocks. Shadowing occurs when a variable in an inner block has the same name as a variable in an outer block.
The inner variable takes precedence. Any reference to that name inside the inner block refers to the new, inner variable. The outer variable becomes inaccessible. This is not always an error, but it often is a mistake.
Understanding Go Variable Scope in for Loops Explained is key to avoiding these issues in iterative code.
A Simple Shadowing Example
Let's look at a basic example. We have a function that should modify an outer variable.
package main
import "fmt"
func main() {
count := 10 // Outer variable
if true {
count := 5 // This SHADOWS the outer 'count'
fmt.Println("Inner count:", count)
}
fmt.Println("Outer count:", count) // This still prints 10!
}
Inner count: 5
Outer count: 10
The inner count := 5 creates a new variable. It does not assign a new value to the outer count. The outer variable remains unchanged. This is the core of the shadowing problem.
Common Causes of Shadowing
Shadowing often happens in specific situations. Being aware of these patterns helps you catch mistakes early.
1. Re-declaring with Short Assignment
The short variable declaration operator := is a common culprit. It will declare a new variable if a variable of that name does not already exist in the current block. If you are not careful, you create a shadow.
func calculate() error {
result, err := doThing() // 'err' is declared here
if err != nil {
return err
}
// Later in the same function block...
data, err := doAnotherThing() // This SHADOWS the first 'err'!
// The first 'err' variable is now inaccessible here.
_ = data
return nil
}
For a deeper dive into declaration syntax, see Go Variable Declaration: var vs := Explained.
2. Loop Variables
Variables declared in a for loop header have a scope limited to that loop. Using the same name outside can be confusing, but using it incorrectly inside with := causes shadowing.
items := []string{"a", "b", "c"}
var foundItem string
for _, item := range items {
// Intending to assign to the outer 'foundItem'?
foundItem := item // NO! This creates a new, loop-scoped 'foundItem'.
fmt.Println(foundItem)
}
// Outer 'foundItem' is still empty!
fmt.Println("Found:", foundItem)
a
b
c
Found:
3. Function Parameters and Named Returns
Function parameters are variables in the function's scope. Declaring a new variable with the same name inside the function shadows it.
func process(id int) (result string, err error) {
// 'id' and 'err' are already declared as named return.
id := id * 2 // Shadows the parameter 'id'. Usually wrong.
err := fmt.Errorf("problem") // Shadows the named return 'err'!
return // Returns the shadowed (and likely zero-value) 'err'.
}
How to Find and Fix Shadowing
Go's compiler does not warn about shadowing by default. You need to use a tool or be vigilant.
Using the `vet` Tool with `shadow` Check
The go vet command is a static analysis tool. It can check for shadowing when you enable the `shadow` analysis.
go vet -shadow ./...
This command will scan your project and report potential shadowing variables. Always run this check as part of your development workflow.
Fixing the Mistake
Once you find a shadow, the fix is usually simple: use assignment (=) instead of declaration (:=).
Before (Shadowing):
var config Config
config, err := loadConfig() // Wrong if 'config' exists above.
After (Correct):
var config Config
var err error
config, err = loadConfig() // Correct assignment to existing variables.
If you need to manage short-lived variables cleanly, review Go Best Practices for Temporary Variables.
Best Practices to Prevent Shadowing
Follow these tips to avoid shadowing from the start.
1. Use Distinct Variable Names: Give variables clear, unique names within their logical context. Avoid reusing common names like err, v, or result for different purposes in nested scopes.
2. Prefer `var` for Explicit Declaration: Using var makes it clear you are declaring a new variable. It can help you think about scope. The short declaration := is convenient but riskier.
3. Keep Blocks Short and Functions Small: The longer and more nested your code, the easier it is to lose track of variables. Small functions with limited scope naturally reduce shadowing opportunities.
4. Run `go vet -shadow` Regularly: Integrate this check into your CI/CD pipeline or editor. Catch shadows automatically before they cause bugs.
Conclusion
Variable shadowing is a common pitfall for Go developers. It creates bugs where variables do not update as expected. The key is to understand Go's scoping rules.
Remember that := declares a new variable in the current scope. Use the go vet -shadow tool to find hidden shadows. Prefer assignment (=) when working with existing variables.
By writing clear, short functions and using descriptive names, you can minimize this issue. Mastering variable scope will make you a more effective Go programmer. Your code will be more predictable and easier to debug.