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/setblocks) 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 — thevaris 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.