← Files GophersARCHIVED FILE
skills/go-declarations/references/scope-and-shadowing.md
2.09 KB · Oct 4, 2026 · 12:30 UTC
# Scope and Shadowing
Go has lexical scoping with block-level granularity. The short declaration `:=` interacts with scope in a way that surprises newcomers and seasoned engineers alike.
## Rule of `:=` Redeclaration
`:=` may redeclare a variable, but only when **all three** hold:
1. The redeclaration is in the **same scope** as the existing variable.
2. The value is **assignable** to that variable's type.
3. At least **one other variable** on the left is new.
```go
f, err := os.Open(name) // declares f and err
d, err := f.Stat() // declares d, reassigns err — same scope, OK
```
## The Shadowing Trap
When the inner scope already contains the name, `:=` creates a **new** variable. The outer name is unchanged after the block ends:
```go
ctx := context.Background()
if needTimeout {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
// this inner ctx dies at the closing brace
}
doWork(ctx) // still the original Background context!
```
### Fix
Assign with `=`, declaring any new locals separately:
```go
var cancel context.CancelFunc
if needTimeout {
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
defer cancel()
}
doWork(ctx)
```
## Scope by Construct
| Construct | Variables live until |
|---|---|
| Package | Program exit |
| Function | Function return |
| Block `{ ... }` | Closing `}` |
| `if`/`for`/`switch` init | End of the statement (including all branches) |
| `for` range/clause | End of the loop body |
## Reducing Scope Deliberately
Pull a variable into the narrowest scope where it is meaningful:
```go
// Wider scope than necessary
err := os.WriteFile(name, data, 0644)
if err != nil {
return err
}
return nil
// Tighter
if err := os.WriteFile(name, data, 0644); err != nil {
return err
}
return nil
```
Don't fight indentation, though — if narrowing scope creates a deeply nested success path, prefer the wider scope.
## Tools
- `go vet -shadow` (or `shadow` in golangci-lint) catches the common cases.
- Code review: any `:=` introducing a `ctx` or `err` that shares a name with the function's outer one deserves a second look.
SHA-256: 53270486e03a4f2f64df82665c9fe36aa0a2a2037a94beea445130633ee4295d