shadowing
programming concepts
variable scope
coding practices
software development

What is the problem with shadowing names defined in outer scopes?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Shadowing occurs when a variable in an inner scope has the same name as a variable in an outer scope, hiding the outer one. This creates bugs when the programmer intends to use the outer variable but accidentally references the inner one, or vice versa. The problems include silent behavior changes (the code runs but produces wrong results), difficulty debugging (the bug is invisible without careful scope analysis), and reduced readability. Most linters (Pylint, ESLint, Clippy) warn about shadowing by default.

How Shadowing Works

Python

python
1x = 10  # Outer scope
2
3def process():
4    x = 20  # Shadows outer x — no error, no warning at runtime
5    print(x)  # 20
6
7process()
8print(x)  # 10 — outer x is unchanged

The inner x is a completely separate variable. Assigning to it does not modify the outer x.

JavaScript

javascript
1let count = 0;
2
3function increment() {
4    let count = 0;  // Shadows outer count
5    count++;
6    console.log(count);  // Always 1
7}
8
9increment();
10increment();
11console.log(count);  // 0 — outer count was never modified

Java

java
1public class Example {
2    private int value = 10;
3
4    public void process() {
5        int value = 20;  // Shadows the field
6        System.out.println(value);       // 20 (local)
7        System.out.println(this.value);  // 10 (field)
8    }
9}

The Problems

Problem 1: Silent Bugs

python
1total = 0
2items = [10, 20, 30]
3
4for item in items:
5    total = 0  # Bug: accidentally resetting total each iteration
6    total += item
7
8print(total)  # 30, not 60

The programmer may have intended to reset a different variable. The code runs without errors but produces incorrect results.

Problem 2: Losing Access to Outer Variable

python
1data = [3, 1, 4, 1, 5]
2
3def process(data):  # Parameter shadows module-level 'data'
4    # Cannot access the module-level 'data' here
5    return sorted(data)
6
7result = process([9, 2, 7])
8# What if you need the original module-level data inside the function?

Problem 3: Confusing Closures

javascript
1function createCounters() {
2    let i = 0;
3    const counters = [];
4
5    for (let i = 0; i < 3; i++) {  // Shadows outer i
6        counters.push(() => i);
7    }
8
9    console.log(i);  // 0 — outer i was never incremented
10    return counters;
11}

Problem 4: Exception Handling

python
1result = None
2
3try:
4    result = compute()
5except ValueError as result:  # Shadows outer 'result'!
6    print(f"Error: {result}")
7
8# In Python 3, 'result' is deleted after the except block
9print(result)  # NameError in Python 3!

In Python 3, the exception variable is explicitly deleted after the except block exits, which can delete the outer variable if it was shadowed.

Language-Specific Behavior

LanguageShadowing AllowedCompiler/Linter WarningAccess Outer
PythonYesPylint W0621global/nonlocal keyword
JavaScriptYes (let/const)ESLint no-shadowRename variable
JavaYes (locals shadow fields)IDE warningthis.field
RustYes (explicitly supported)No warning (by design)Not possible
GoYesgo vet / shadow linterRename variable
C++Yes-Wshadow flag::variable for globals

Rust: Intentional Shadowing

Rust is unique — shadowing is an idiomatic pattern:

rust
1let x = "42";       // x is a &str
2let x = x.parse::<i32>().unwrap();  // x is now an i32 — same name, different type
3
4let mut data = vec![3, 1, 4];
5data.sort();
6let data = data;  // Re-bind as immutable after sorting

This avoids naming temporary variables (x_str, x_int) and makes immutability transitions explicit.

How to Avoid Shadowing Bugs

python
1# BAD: parameter shadows built-in
2def process(list, input):  # Shadows built-in list() and input()
3    return list
4
5# GOOD: use descriptive names
6def process(items, user_input):
7    return items
python
1# BAD: loop variable shadows outer
2name = "Alice"
3for name in ["Bob", "Charlie"]:  # Shadows outer 'name'
4    print(name)
5print(name)  # "Charlie" — outer 'name' was overwritten
6
7# GOOD: use a different loop variable
8name = "Alice"
9for person in ["Bob", "Charlie"]:
10    print(person)
11print(name)  # "Alice"

Common Pitfalls

  • Shadowing built-in names (list, dict, id, type, input): Reusing built-in names as variable names hides the built-in function for the rest of the scope. Calling list() after assigning list = [1, 2, 3] raises TypeError: 'list' object is not callable.
  • Assuming shadowed variables modify the outer scope: In Python, assigning to a variable inside a function creates a local variable. It does not modify the outer variable. Use nonlocal (for enclosing function scope) or global (for module scope) to modify outer variables explicitly.
  • Ignoring linter warnings about shadowing: Pylint's W0621 (redefined-outer-name) and ESLint's no-shadow rule exist to prevent real bugs. Suppressing these warnings without understanding them leads to subtle errors.
  • Exception variable shadowing in Python 3: except ValueError as e deletes e after the except block exits. If e was also used as a variable name in the outer scope, it gets deleted. Use a unique name for exception variables.
  • Confusing Rust's intentional shadowing with accidental shadowing in other languages: Rust's let x = ... rebinding is a deliberate language feature that enables type changes and immutability transitions. In Python, JavaScript, and Java, shadowing is almost always accidental and should be avoided.

Summary

  • Shadowing hides an outer variable with an inner one of the same name, creating silent bugs
  • The main risks are wrong results, lost access to outer variables, and confusing closures
  • Use descriptive variable names and avoid reusing built-in names
  • Enable linter rules (no-shadow, W0621, -Wshadow) to catch shadowing early
  • In Rust, shadowing is idiomatic and safe; in most other languages, it is a code smell

Course illustration
Course illustration

All Rights Reserved.