Variables, Scopes & Environments
Variable types
| Kind | Scope | Default |
|---|---|---|
| Global | Entire program | nil if unset |
| Local | Lexical block | Declared with local |
| Upvalue | Captured by closure | Outer local variable |
Global variables
Any variable not declared local is global. Globals live in the environment table _ENV.
x = 10 -- global: same as _ENV.x = 10
print(x) -- 10
Free names → _ENV
Every chunk gets an implicit local _ENV = <environment>. Any free name var is sugar for _ENV.var.
-- These are equivalent:
print("hi")
_ENV.print("hi")
[!important]
_ENVis a local_ENVis a regular local variable. You can reassign it to sandbox code:local _ENV = {print = print, pairs = pairs}print("sandboxed!") -- worksos.execute("rm -rf /") -- error: os is nil
Local variables
Declared with local keyword. Scope extends from declaration to end of enclosing block.
do
local x = 10
print(x) -- 10
end
print(x) -- nil (out of scope, or global x if defined)
Scope rules
- Scope starts after the declaration
- Idiomatic to declare locals as close to use as possible
localin inner block shadows outer variable
local x = 10
do
local x = 20 -- shadows outer x
print(x) -- 20
end
print(x) -- 10
Declaration forms
local a -- initialized to nil
local b = 1 -- single value
local c, d = 1, 2 -- multiple assignment
local e, f = 1, 2, 3 -- extra values discarded
local g, h = 1 -- h = nil (missing values)
Assignment
Single assignment
x = 10
t.name = "Lua"
Multiple assignment
a, b = 10, 20
a, b = b, a -- swap!
a, b, c = 1, 2 -- c = nil
a, b = 1, 2, 3 -- 3 discarded
[!tip] Multiple assignment evaluates all right-side expressions first, then assigns left-to-right.
a, b = f() -- f() can return multiple values
Environments & _G
The global environment is the default table for _ENV. It's accessible as _G:
print(_G == _ENV) -- true (in main chunk)
Sandbox example
local safe_env = {
print = print,
pairs = pairs,
ipairs = ipairs,
type = type,
tostring = tostring,
tonumber = tonumber,
math = math,
string = string,
table = table,
}
local f = load("return math.sqrt(16)", "sandbox", "t", safe_env)
print(f()) -- 4.0
_ENV and modules
The standard module pattern manipulates _ENV:
local M = {}
local _ENV = M -- all free names now refer to M
function add(a, b)
return a + b
end
return M
Scoping diagram
┌─ chunk (global scope) ──────────────────┐
│ _ENV = _G │
│ global_var = ... │
│ │
│ ┌─ function f() ──────────────────┐ │
│ │ local upval = ... (upvalue) │ │
│ │ │ │
│ │ ┌─ do block ──────────────┐ │ │
│ │ │ local inner = ... │ │ │
│ │ │ -- can see: upval, │ │ │
│ │ │ -- inner, _ENV │ │ │
│ │ └─────────────────────────┘ │ │
│ │ -- inner is out of scope │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘