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.