Introduction
One odd thing about Lua when compared to other programming languages is, everything is global by default. Take this for example:
function func_a()
q_var = 7
end
func_a()
print(q_var)
Once func_a
is called q_var
is available and set to 7
. Even outside of
func_a
. You can mark a variable or function as local
so it’s not global and
pretty much anything written in Lua that’s mildly complex will use local
a
lot. Basically, global scope is default and anything that should be local
scope needs to be explicitly marked.
The For Loop
for
loops are a special case in Lua when it comes to global scope.
Interestingly, the variables that are part of the loop declaration
are local to the loop itself. Without needing to be marked as such.
Let’s look at an example.
local c = { 1, 2, "a", "b" }
for _, v in ipairs(c) do
print(_, v)
end
print(_, v)
You’d expect the last print(_, v)
to have the last values from the for
loop
but they don’t! They’re both nil
. Now, you might think Lua’s just clearing
the variables when the loop exits but that isn’t what’s happening.
A for
loop is truly locally scoped when it comes to the variables that
are part of the declaration. The outer ones aren’t modified at all and
are simply “masked” in the loop itself. Here’s a demonstration.
local c = { 1, 2, "a", "b" }
local _ = "Underscore"
local v = "V"
print(_, v)
for _, v in ipairs(c) do
print(_, v)
end
print(_, v)
We’ve set both _
and v
, and then we run the loop. Inspecting the output…
$ lua r.lua
Underscore V
1 1
2 2
3 a
4 b
Underscore V
Inside of the loop _
and v
are the expected values coming from iparis
.
However, once the loop exits both variables are back to their original values
from outside of the loop.
This isn’t exclusive to ipairs
either. The same is true for pairs
or even
counting (for v=1,10 do ... end
). Basically, even though local
isn’t used,
Lua is treating these variables as if they were declared within the loop as
local
.
Conclusion
So, what does this all mean?
First, you can’t get the last value from the variables declaring a for
loop.
Meaning, if you break
early and you need the value you must set it to a
different variable. One not marked local
within the loop, but you can use a
global in the loop and it will still be accessible once the loop exits. Though,
you probably should declare a local
variable outside of the loop.
Second, you don’t have to declare the variables
as local
outside of the loop. These variables are not created or internally treated
as a global so you don’t have to worry about them polluting the global
scope.
Third, if you have a variable and reuse it in a for
loop declaration
it will be masked within the loop. The variable will be unaltered by
the loop and will have it’s original value once the loop ends.