How the `forvar` Guideline Prevents Loop Variable Shadowing in go-modern-guidelines

The forvar guideline eliminates loop variable shadowing by forbidding redundant x := x declarations inside for loops, ensuring the iteration variable remains accessible and preventing subtle capture bugs.

The forvar rule (ID 3.2) in the JetBrains/go-modern-guidelines repository targets a specific anti-pattern where developers re-declare loop variables using short variable declaration syntax. This guideline ensures code clarity and prevents bugs related to variable capture, particularly when dealing with goroutines or closures inside loops.

Understanding Loop Variable Shadowing in Go

Loop variable shadowing occurs when a for loop iteration variable is hidden by a new variable with the same name declared inside the loop body. This typically happens when developers mistakenly use the := operator to "copy" the iteration variable:

for _, v := range values {
    v := v               // ← shadows the iteration variable
    // … use v …
}

When the inner v := v statement executes, it creates a fresh local variable that shadows the original iteration variable. The outer v becomes inaccessible within that block, which can lead to subtle bugs especially when the variable is captured by goroutines or closures.

The forvar Guideline Implementation

The guideline is formally defined in the repository's FEATURES.md (around line 471) and encoded in internal/guidelines/guidelines.json. It specifically targets redundant variable declarations within loop constructs.

Detection Logic

According to the go-modern-guidelines source code, the rule detects short variable declarations (:=) where the right-hand side identifier matches the left-hand side identifier inside a for loop body. This pattern always indicates unnecessary shadowing of the iteration variable.

The directive instructs developers to remove the redundant x := x declaration and use the loop variable directly. For scenarios requiring value capture—such as launching goroutines inside loops—the guideline recommends passing the variable as an argument rather than shadowing it:

// Incorrect: shadows the loop variable
for _, v := range values {
    v := v
    go func() {
        fmt.Println(v)    // Likely prints the last value only due to capture
    }()
}

// Correct: pass the iteration variable as an argument
for _, v := range values {
    go func(val int) {
        fmt.Println(val)
    }(v)
}

Rationale and Go Version Requirements

As implemented in the JetBrains/go-modern-guidelines source, the forvar rule addresses the specific issue where re-declaring loop variables can cause accidental capture of the wrong value. The guideline is enforced starting with Go 1.22, reflecting modern Go best practices for loop variable semantics. Removing these redundant declarations improves readability and ensures the identifier always refers to the actual iteration value.

Code Examples: Before and After

Consider the following patterns that violate the forvar guideline:

for i, item := range list {
    item := item          // shadows the iteration variable
    fmt.Println(item)     // Works but fragile and non-compliant
}

After applying the forvar fix:

for i, item := range list {
    // Use the iteration variable directly
    fmt.Println(item)
}

For concurrent code, the distinction is critical. The shadowing pattern often masks the bug where all goroutines reference the same memory location:

// Violates forvar: creates a new variable that masks iteration semantics
for _, v := range values {
    v := v
    go func() {
        use(v)            // Unsafe capture of shadowed variable
    }()
}

The corrected implementation follows the guideline by eliminating the shadowing declaration and explicitly capturing the value:

// Compliant with forvar
for _, v := range values {
    go func(val int) {
        use(val)
    }(v)
}

Source Files and Tooling

The guideline definition resides in key locations within the repository:

  • FEATURES.md – Contains the formal rule description, status table, and examples in section 3.2 (approximately line 471).
  • internal/guidelines/guidelines.json – Machine-readable JSON representation of all guidelines used by automated tooling.
  • internal/cli/cli.go – CLI driver that reads the guidelines JSON and reports forvar violations during code analysis.

These files collectively define, enforce, and document how the forvar guideline eliminates loop variable shadowing throughout the codebase.

Summary

  • The forvar guideline (ID 3.2) targets redundant x := x declarations inside for loops that shadow iteration variables.
  • It is defined in FEATURES.md and encoded in internal/guidelines/guidelines.json within the JetBrains/go-modern-guidelines repository.
  • The rule prevents subtle bugs caused by variable capture in goroutines and closures.
  • Starting with Go 1.22, the guideline enforces direct use of loop variables without shadowing.
  • The fix involves removing the redundant declaration or explicitly passing values as function arguments.

Frequently Asked Questions

What specific pattern does the forvar guideline detect?

The guideline detects short variable declarations using := where the identifier on the left-hand side matches the identifier on the right-hand side within a for loop body. This pattern creates a new local variable that shadows the loop's iteration variable, making the original inaccessible within that scope.

Why is loop variable shadowing problematic in Go?

Shadowing the iteration variable prevents direct access to the actual loop value and can cause incorrect variable capture in goroutines. When developers reference the shadowed variable inside a closure, they often unintentionally share the same memory address across iterations, leading to race conditions or logical errors where only the final value is processed.

How do I fix code that violates the forvar guideline?

Remove the redundant v := v style declaration and use the iteration variable directly. If you need to capture the current value for concurrent processing, pass it as an explicit argument to the function or goroutine rather than relying on variable shadowing or closure capture.

Which Go versions support the forvar guideline?

The guideline is enforced starting with Go 1.22 according to the feature definitions in the JetBrains/go-modern-guidelines repository. This aligns with changes in Go's loop variable semantics introduced in recent versions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →