Why set the control variable of this for loop to read-only in Lua5.5?

89 views
Skip to first unread message

Cheese

unread,
Dec 23, 2025, 4:18:01 AM (yesterday) Dec 23
to lua-l
Dear maintainers of the Lua project
I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.

In other language like java:
I can do

```java
for (int i=0; i<10; i++) {
   // some logic
   if (xxx) {
        i += 2; // skip 3 loop
    }
}
```

But in lua5.5

```lua
j=0
for i=1,10 do
  if j>0 then
    goto continue
  end
  -- some logic
  if xxx then
    j=2
  end
  ::continue::
end
```

I need to introduce another local variable to skip 2 loops.

But in lua5.4 or older

for i=1,10 do
  if xxx then
    i=i+2
  end
  ::continue::
end

I think this restriction is completely unnecessary.
In many programming languages, we can modify the control variables in the loop.

Xmilia Hermit

unread,
Dec 23, 2025, 4:23:21 AM (yesterday) Dec 23
to lu...@googlegroups.com
Cheese:
> But in lua5.4 or older
>
> for i=1,10 do
>   if xxx then
>     i=i+2
>   end
>   ::continue::
> end

Because this code does not what you want. It does not change the
iteration variable. This is just syntactic sugar for

local private_i = 1
while private_i <= 10 do
local i = private_i
if xxx then
i=i+2
end
private_i = private_i + 1
end

so your update on i is only in the current loop. And to make this clear
it was changed.

Regards,
Xmilia

Luiz Henrique de Figueiredo

unread,
Dec 23, 2025, 4:46:26 AM (yesterday) Dec 23
to lu...@googlegroups.com
> I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.

It's been like that since Lua 5.1.
Take this program:

for i=1,10 do
print(i)
if i==4 then i=7 end
end

Here is its output in Lua 4 and Lua 5:

-- Lua 4.0.1
-- Lua 5.0.3
1
2
3
4
8
9
10
-- Lua 5.1.5
-- Lua 5.2.4
-- Lua 5.3.6
-- Lua 5.4.8
1
2
3
4
5
6
7
8
9
10
-- Lua 5.5.0
i:3: attempt to assign to const variable 'i'

Spar

unread,
Dec 23, 2025, 4:55:03 AM (yesterday) Dec 23
to lu...@googlegroups.com
For the historical background, why was it shadowed in 5.1?
--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/lua-l/CABt16q%3DOeM5FDZcQJtD4bkcvAbDFs-ReD1EySzt6S3vA7%3D1Cfg%40mail.gmail.com.

Philippe Verdy

unread,
Dec 23, 2025, 6:13:22 AM (yesterday) Dec 23
to lu...@googlegroups.com
That's a stupid decision: it still creates an additional (hidden) local variable, but in the wrong (outer) lexical scope, instead of the inner scope of the loop statement itself. So this does not save anything.

The for loop:
```for i=1,10 do
  if xxx then
    i=i+2
  end
  ::continue::
end```

should not be at all (using hidden variable j with the incorrect scope):

```j=0
for i=1,10 do
  if j>0 then
    goto continue
  end
  -- some logic
  if xxx then
    j=2
  end
  ::continue::
end````

but should have been equivalent to this syntaxic sugar equivalent:

```do
  local i=1 // loop-control variable initializer (first parameter of the 'for' statement)
  while i < 10 do // 
      -- some logic
      if xxx then
        i = i + 2 // skip 3 loops
      end
  end
  ::continue::
  i = i + 1 // implicit increment (third parameter of the 'for' statement)
end```

(and no need in fact to introduce a label pseudo statement '::continue::' to make it work)

For the compiler this just means creating a lexical scope (like the outer do... end above) to embed the for statement. And it matches exactly what other OO languages like Java or C++ are doing, without needing to complexify the source (and in fact the first equivalent using an extra variable j is just wrong, it is not equivalent at all (it does NOT skip 3 loops, once the variable j is set to 2, it remains equal to 2 and still performs the loops using goto continue each time till the end of the 10 loops, so it can skip up to 10 loops, not just 3)

--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Sewbacca

unread,
Dec 23, 2025, 7:41:59 AM (yesterday) Dec 23
to lu...@googlegroups.com
I wouldn't call this decision stupid, but I agree with the core of the sentiment.

for loops had this IMO usefull quirk that you could change the iteration variable without changing the loop structure.

You could argue that it was misleading and making it constant now will show unintentional bugs in your code, but it will also break intentional uses of this quirk. And every implementation detail that stays around for long enough will eventually become part of the API.

If we break scripts anyways, why not leverage this change and allow us to modify the iteration variable instead? Change it to what it should have been since 5.1: a way to control the for loop during execution.

There's however another reason I want to mention here:
for loops are guaranteed to break, except if one limit is math.huge (not sure about NaN). That is even if you get an arithmetic over or underflow, the loop will still break preventing an infinite loop. Granted this happens irregularly, but if it happens it could be a devastating bug.

~ Sewbacca

ImagicTheCat

unread,
Dec 23, 2025, 8:32:25 AM (yesterday) Dec 23
to lu...@googlegroups.com
Hi,

> for loops had this IMO usefull quirk that you could change the iteration variable without changing the loop structure.

Take my opinion with a grain of salt as I almost exclusively use
LuaJIT and I am not a professional developer, but I don't see it as a
quirk. I have been bitten in the past by the scoping rules of
JavaScript when creating closures in for-loops and I was glad to
discover that Lua did it right (IMO): all loop variables are just new
locals for the scope of the current iteration. So, I personally see
this read-only constraint as a regression from an elegant design,
maybe to handle a (common?) misunderstanding that I never really
experienced (maybe once). (And when there is a need for more control,
I will just use a while-loop.)

-- Imagic

Augusto Goulart

unread,
Dec 23, 2025, 9:40:02 AM (yesterday) Dec 23
to lu...@googlegroups.com
I’d just like to say this discussion started with a misunderstanding of the purpose of for/while loops. If you know start, end, steps: for; if you must check a condition: while. And just because you CAN do it in this or that language doesn’t mean you SHOULD do it. Just because you can do for(;;ch=getchar()) in C does not mean you should do it.

Augusto

--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Roberto Ierusalimschy

unread,
Dec 23, 2025, 10:30:49 AM (yesterday) Dec 23
to lu...@googlegroups.com
> For the historical background, why was it shadowed in 5.1?

I am not sure that was the reason in 5.1, but at least for more recent
versions that semantics allows a slightly more efficient implementation.

A for loop does not know, in general, whether the step is positive
or negative at compile time. So it would have to check that at each
step. What Lua does, instead, is that it computes once, when it
enters the loop, how many steps the loop will run. After that, each
iteration only decrements that count and checks for zero, which is
quite efficient. However, that implies that the value of the so-called
"control variable" does not control anything after the loop starts.

-- Roberto

Roberto Ierusalimschy

unread,
Dec 23, 2025, 10:36:17 AM (yesterday) Dec 23
to lu...@googlegroups.com
BTW, making the variable read-only also allows a small optimization, as
the code does not need to keep an extra copy of the control variable.

-- Roberto

Francisco Olarte

unread,
Dec 23, 2025, 12:32:25 PM (yesterday) Dec 23
to lu...@googlegroups.com
On Tue, 23 Dec 2025 at 10:18, Cheese <justlike...@gmail.com> wrote:
Dear maintainers of the Lua project
I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.
....
I think this restriction is completely unnecessary.
In many programming languages, we can modify the control variables in the loop.

And in many others you can not. The restriction makes the special properties ( since 5.1 ) of the control variable explicit and helps avoid mistakes. Letting you modify it and then forgetting modifications can lead to puzzling behaviour. It is unnecessary, but it will probably be a lurking mousetrap even for me. 

I like it better this way. As said, just local i=i gets you the 5.4 behaviour. So if I typo-modify by mistake I get an error, if I see the telling local i=i I know I have to be extra careful.

Not having a generic loop makes the language simpler. Lua goes for that in many places. And emulating it with a while, and a surrounding block if you want c++/java declaration, is simple for people who need it. And the resulting code is many times easier, I have lost count of the times I have started with a generic pointer chasing or similar generic for loop, had to modify control variables inside, leaned back and refactored it to a while for my own sake on future maintenance work.

Francisco Olarte.

Cheese

unread,
7:21 AM (8 hours ago) 7:21 AM
to lua-l
I am sorry that I forget it. Thank you all for your replies. I hope lua will get better and better in the future. Thank you so much for taking the time to reply despite your busy schedules. I now understand the change😉.
QQ20251224-201437.png

Marc Balmer

unread,
8:00 AM (7 hours ago) 8:00 AM
to lu...@googlegroups.com, lua-l


> I hope lua will get better and better in the future.

While everything gets better, Lua remains good.

Philippe Verdy

unread,
10:52 AM (5 hours ago) 10:52 AM
to lu...@googlegroups.com
Le mer. 24 déc. 2025 à 13:21, Cheese <justlike...@gmail.com> a écrit :
I am sorry that I forget it. Thank you all for your replies. I hope lua will get better and better in the future. Thank you so much for taking the time to reply despite your busy schedules. I now understand the change😉.
QQ20251224-201437.png
在2025年12月24日星期三 UTC+8 01:32:25<Francisco Olarte> 写道:
On Tue, 23 Dec 2025 at 10:18, Cheese <justlike...@gmail.com> wrote:
Dear maintainers of the Lua project
I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.
....
I think this restriction is completely unnecessary.
In many programming languages, we can modify the control variables in the loop.

And in many others you can not. The restriction makes the special properties ( since 5.1 ) of the control variable explicit and helps avoid mistakes. Letting you modify it and then forgetting modifications can lead to puzzling behaviour. It is unnecessary, but it will probably be a lurking mousetrap even for me. 

I like it better this way. As said, just local i=i gets you the 5.4 behaviour.

It does not allow changing the number of loops, notably to skip some steps in the given example, Many data parsers (notably with variable-length structrures, or many cases were you need to support backtracking) require this.

And even with the "justification" give nthat the loop is "warrantied" to break, this is false, this has never been defined like this and not even been tested (e.g. with overflows/underflows), so this argument just assumes a specific undefined behavior. If you want to warranty that the loop will end, this still needs to be checked explicitly before starting any loops (even if you don't ever modify the control variable inside the loop).

My opinion is that this new requirement was made only to allow some hardware-specific optimizations in some Lua implementations (e.g. allowing them to unroll some loops to process multiple items at the same time or in parallel), but these optimizations are unsafe (and it is tricky to make it safe, e.g. to respect things like memory address alignment and use vector extensions, and also ensure that this does not create other risks such as side-channel time attacks which may occur when using some optimistic branch prediction, and is impossible to protest from is there's support in the Language for memory fences and for strict isolation of resources usage, between distinct security realms: Lua has no mechanism for that, it is running inside a single system thread and offers no control on its own process face to other concurrent processes, or concurrent threads that may be used and not necessarily running a Lua engine, e.g. a webserver worker thread, or some I/O worker service).

If such optimimisation were intended, we don't know for which platform this was done, and for which intent (which means that this is also undefined behavior), but it seemed to work for that unkown intent and there's no way to check it. The internal compiler may already be very unsafe and actually are too poorly developed to support that, or it would have requried extensive work to obtain that goal and the initial implementation would not have met the goal before a long time and costly development of this uunknown specific Lua VM implementation.

Pan

unread,
12:19 PM (3 hours ago) 12:19 PM
to lu...@googlegroups.com
I think a better example exists given that, at least
when it comes to the numeric for, you're usually much better off using a
while/repeat or a good step value if you will change it after. BUT
there is a common pattern that exists and takes the control vars
locality/mutability into account and uses it for its advantage.


for m in file:gmatch(general_pattern) do
m = m:match(actual_pattern)
if m then
...
end
end


This is extremely common given the limitations of lua patterns, the fix
is simple, but annoying, especially if you're just golfing.


I suppose building a tool that inserts a "local <control_var>" after
all for loops is necessary, it being hopefully smart enough to examine
the for loop block and inserting local <control_var> only if the
control_var is reassigned in the block on all lua files, actually, why
wasn't that an option?
Reply all
Reply to author
Forward
0 new messages