Vars Should Be Constants

ID

swift.vars_should_be_constants

Severity

low

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

Swift

Tags

code-smell, mutability, suspicious-code

Description

Reports a function-local var binding whose value is never reassigned. Swift draws a hard line between mutable bindings (var) and constants (let): when the value never changes after the initial assignment, the binding is a constant by intent and should be declared with let.

func sum() -> Int {
    var x = 1                 // FLAW — never reassigned, should be let
    return x + 2
}

func count() -> Int {
    var x = 1                 // OK — reassigned below
    x = 2
    return x
}

func tally(_ xs: [Int]) -> Int {
    var total = 0             // OK — compound-assign target
    for x in xs { total += x }
    return total
}

Rationale

Declaring a binding as let makes intent explicit and lets the compiler reject accidental mutation in future edits. It also unlocks optimizations (the value is known to be invariant) and removes a small but real cognitive load from the reader, who otherwise scans the rest of the function for the mutation that never comes.

Remediation

Replace var with let for any binding that is set once and only read afterwards:

func sum() -> Int {
    let x = 1
    return x + 2
}

If the binding is meant to be reassigned later, the rule already treats both plain = and compound (+=, -=, *=, …​) writes as mutations and will not fire.

When not to fire

The rule is intentionally narrow and skips:

  • type-stored properties (class C { var x = 1 }) — callers may reassign them through references we cannot see;

  • computed properties (get / set blocks) and observable properties (willSet / didSet) — those are not plain stored bindings;

  • tuple destructuring (var (a, b) = …​) — tracking per-leg mutation is out of scope for v1;

  • for var x in …​ loop variables — the var is on the loop pattern, not on a function-local declaration;

  • any binding whose enclosing scope contains a plain = or a compound assignment whose left-hand side resolves to the bound name.