How to make a for loop variable const with the exception of the increment statement?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In C and C++, the loop variable in a for loop is mutable by design — it needs to be incremented each iteration. But you may want to prevent accidental modification of the loop variable inside the loop body while still allowing the increment in the for statement itself. Unfortunately, there is no direct language feature that makes a variable "const except for the increment." However, several patterns come close: range-based for loops, const references to the loop counter, and C++20 ranges.
The Problem
Making i const would prevent the increment in ++i, so for (const int i = 0; ...) does not compile.
Solution 1: Range-Based For with const (C++11)
The cleanest approach — iterate over a range with a const reference:
This works because the range-based for loop creates a new const int i each iteration, initialized from the next element.
Solution 2: C++20 std::views::iota
C++20 provides a lazy range that avoids allocating a vector:
This is the most idiomatic modern C++ solution — no allocation, lazy evaluation, and the loop variable is truly const.
Solution 3: Lambda with Const Parameter
Wrap the loop body in a lambda that takes the index by const value:
This shadows the mutable i with a const copy inside the lambda. The outer i is still mutable for the increment.
Solution 4: Separate Function
Extract the loop body into a function that takes const parameters:
This is the simplest refactoring — the function signature documents that the index is read-only.
Solution 5: Inner Scope with const Copy
Create a const copy inside the loop body:
This protects against accidental use of idx but does not prevent direct modification of i. It is a convention-based approach.
Solution 6: std::for_each with Index
When you need the index:
Other Languages
JavaScript (const in for-of)
Rust (Immutable by Default)
Rust variables are immutable by default, so the loop variable is already protected. You need mut to make it mutable.
Go
Common Pitfalls
constin traditional for loops:for (const int i = 0; i < 10; ++i)does not compile because++imodifiesi. The traditionalforloop requires a mutable counter.- Performance of std::views::iota:
std::views::iotais lazy and has zero allocation overhead. It is just as efficient as a rawforloop. Do not avoid it for performance reasons. - Lambda capture vs parameter: If you use
[i](capture by value) instead of takingconst int ias a parameter, the capturediisconstby default in the lambda body (unless you usemutable). But this is less explicit than aconstparameter. - Shadowing confusion: The lambda/inner-scope approaches create a new variable that shadows the loop counter. This can be confusing if someone expects
iandidxto be different values. Use clear naming. - C++20 availability:
std::views::iotarequires C++20 and a conforming standard library. If you are stuck on C++11/14/17, use a vector withstd::iotaor write a simplerange()utility.
Summary
- Use
for (const int i : std::views::iota(0, n))in C++20 for a truly const loop variable - Use
for (const int i : range_vector)in C++11 with a pre-built vector - Extract the loop body into a function taking
const intfor a simple, portable solution - Use a lambda with a
constparameter to shadow the mutable counter - In Rust, loop variables are immutable by default — no extra work needed
- In JavaScript,
for (const x of iterable)gives a const binding each iteration

