Scope Tutorial |
|
local
keyword.
The examples in this page will be written in the form of a script file instead of an interactive interpreter session, since local variables are really hard to work with in it. It will be explained why later.
To create local variables, add the local
keyword before the assignment:
local a = 5 print(a)
You don't need the local keyword any more when changing the variable:
local a = 5 a = 6 -- changes the local a, doesn't create a global
Local variables only exist in the block they were created in. Outside of the block, they do not exist any more.
local a = 5 print(a) --> 5 do local a = 6 -- create a new local inside the do block instead of changing the existing a print(a) --> 6 end print(a) --> 5
The place where a variable is visible is called the "scope" of a variable.
Now let's use functions to show how this is really useful:
function bar() print(x) --> nil local x = 6 print(x) --> 6 end function foo() local x = 5 print(x) --> 5 bar() print(x) --> 5 end foo()
As you can see, each variable is visible from the point where it's declared to the end of the block it's declared in. Even though bar
's x exists at the same time as foo
's x, they're not written in the same block, so they're independent. This is what's called lexical scoping.
local function
syntax sugar
local function f() end -- is equivalent to local f f = function() end -- not local f = function() end
the difference between the last two examples is important: the local variable still doesn't exist to the right of the =
that gives it the initial value. So if the contents of the function used f
to get a reference to itself, it will correctly get the local variable in the first and second versions, but the third version will get the global f (which will be nil, if not a completely unrelated value set by some other code).
Functions can use local variables created outside of them. These are called upvalues. A function that uses upvalues is called a closure:
local x = 5 local function f() -- we use the "local function" syntax here, but that's just for good practice, the example will work without it print(x) end f() --> 5 x = 6 f() --> 6
The function sees the change even if it's changed outside of the function. This means that the variable in the function is not a copy, it's shared with the outer scope.
Also, even if the outer scope has passed, the function will still hold on to the variable. If there were two functions created in the scope, they will still share the variable after the outer scope is gone.
local function f() local v = 0 local function get() return v end local function set(new_v) v = new_v end return {get=get, set=set} end local t, u = f(), f() print(t.get()) --> 0 print(u.get()) --> 0 t.set(5) u.set(6) print(t.get()) --> 5 print(u.get()) --> 6
Since the two values returned by the two calls to f
are independent, we can see that every time a function is called, it creates a new scope with new variables.
Similarly, loops create a new scope on each iteration:
local t = {} for i = 1, 10 do t[i] = function() print(i) end end t[1]() --> 1 t[8]() --> 8
Because it runs each line in a new scope:
> local a=5; print(a) 5 > print(a) -- a is out of scope now, so global a is used nil
One thing you can do is wrap the code in a do-end block, but it won't be interactive until you finish writing the whole block:
> do >> local a = 5 >> print(a) -- works on a new line >> end 5
You might be coming from another language that makes variables local by default, and are probably thinking "what is the point of all this extra complication? Why not make variables local by default?":
x = 3 -- more code, you might have even forgotten about variable x by now... function () -- ... x = 5 -- does this create a new local x, or does it change the outer one? -- ... end -- some more code...
The problem with changing the outer one is that you might have intended to make a new variable, and instead change the existing one that you might not even know about, introducing bugs.
The problem with creating a new one is what if you actually want to change the outer one?
With the local keyword, it's all explicit: without local
, you change the existing variable, with it, you create a new one.
For more discussion about this, see LocalByDefault.
The general rule is to always use local variables, unless it's necessary for every part of your program to be able to access the variable (which is very rare).
Since it's easy to forget a local
, and since Lua doesn't warn you about it (instead silently creating a global), it can be a source of bugs. One solution is to use a script like strict.lua
(shown below), that uses metatables (mentioned in a later tutorial) to trap global variable creation and raise an error. You can put the script in a file in your project, and do require("strict")
to use it.
-- -- strict.lua -- checks uses of undeclared global variables -- All global variables must be 'declared' through a regular assignment -- (even assigning nil will do) in a main chunk before being used -- anywhere or assigned to inside a function. -- local mt = getmetatable(_G) if mt == nil then mt = {} setmetatable(_G, mt) end __STRICT = true mt.__declared = {} mt.__newindex = function (t, n, v) if __STRICT and not mt.__declared[n] then local w = debug.getinfo(2, "S").what if w ~= "main" and w ~= "C" then error("assign to undeclared variable '"..n.."'", 2) end mt.__declared[n] = true end rawset(t, n, v) end mt.__index = function (t, n) if not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then error("variable '"..n.."' is not declared", 2) end return rawget(t, n) end function global(...) for _, v in ipairs{...} do mt.__declared[v] = true end end
For more info about enforcing use of local variables, see DetectingUndefinedVariables.