Variables and Types in Lua
Lua is a dynamically typed language, meaning you do not need to declare the type of a variable before using it. The type is determined automatically at runtime based on the value assigned. This makes Lua flexible and beginner-friendly, though understanding how types work helps you write better code.
Variables: Local Versus Global
In Lua, variables are containers for values. By default, variables are global unless you explicitly declare them as local.
Global Variables
Global variables are accessible from anywhere in your program:
-- This creates a global variable
message = "Hello, Lua!"
print(message) -- Hello, Lua!
-- You can also use bracket syntax for global access
_G.message = "Hello again!"
Global variables persist throughout your program but can cause naming conflicts in larger projects. Use them sparingly.
Local Variables
Local variables are declared with the local keyword and are only accessible within the block where they are defined:
local count = 10
local name = "Lua"
function greet()
local greeting = "Hi there" -- local to this function
print(greeting .. ", " .. name)
end
greet() -- Hi there, Lua!
-- print(greeting) -- Error: greeting is not defined
Always prefer local variables in Lua. They are faster to access and prevent unintended interactions between different parts of your code.
The _ENV Variable
In Lua 5.2 and later, global variables are actually entries in the _ENV table:
print(_ENV == _G) -- true (in the main chunk)
local env = {}
_G = env -- Redirects global access to a new table
env.x = 10
print(x) -- 10
Understanding _ENV becomes important when you want to control what globals are available, particularly when embedding Lua.
Lua’s Eight Basic Types
Lua has eight basic types: nil, boolean, number, string, function, table, userdata, and thread.
nil
nil represents the absence of a value. Uninitialized variables default to nil:
local x
print(x) -- nil
local y = nil
print(y) -- nil
Checking for nil is common in Lua:
local value = some_function()
if value == nil then
print("No value returned")
end
Boolean
Boolean values are true and false. In Lua, only nil and false are falsy; everything else is truthy, including zero and empty strings:
local is_active = true
local is_empty = false
if is_active then
print("Active!") -- This runs
end
if 0 then
print("Zero is truthy") -- This runs!
end
if "" then
print("Empty string is truthy") -- This runs!
end
Number
Lua uses a single number type that represents both integers and floating-point numbers. Internally, this is typically a 64-bit double:
local integer = 42
local floating = 3.14159
local negative = -10
local scientific = 6.02e23
print(type(integer)) -- number
print(type(floating)) -- number
Lua 5.3+ distinguishes between integers and floats when needed, but you rarely need to think about this distinction in everyday code.
String
Strings in Lua are immutable sequences of bytes. You can use single quotes, double quotes, or long brackets:
local single = 'Hello'
local double = "Hello"
local multiline = [[Hello
World]]
local escaped = "He said, \"Lua is great!\""
print(#single) -- 5 (the # operator gives string length)
The # operator returns the length of a string:
print(#"Lua") -- 3
print(#"") -- 0
print(#"Hello") -- 5
Function
Functions in Lua are first-class values. They can be stored in variables, passed as arguments, and returned from other functions:
local function greet(name)
return "Hello, " .. name .. "!"
end
print(greet("Lua")) -- Hello, Lua!
-- Functions as values
local say_hello = function()
print("Hi!")
end
say_hello() -- Hi!
Table
Tables are Lua’s only composite data structure. They implement arrays, dictionaries, objects, and more:
-- Array-like table
local fruits = {"apple", "banana", "cherry"}
print(fruits[1]) -- apple (tables are 1-indexed!)
-- Dictionary-like table
local person = {
name = "Alice",
age = 30,
city = "London"
}
print(person.name) -- Alice
-- Mixed
local mixed = {
"first",
second = "value",
3
}
Userdata
Userdata represents raw C data. It allows Lua to interface with C structures and is primarily used when embedding Lua in C programs:
-- You cannot create userdata from pure Lua
-- It comes from the C host program
print(type(obj)) -- userdata (when obj is a userdata value)
Thread
Threads represent coroutines, Lua’s lightweight concurrency mechanism:
local co = coroutine.create(function()
print("Running")
end)
print(type(co)) -- thread
Type Checking with type()
The type() function returns the type of a value as a string:
print(type(nil)) -- nil
print(type(true)) -- boolean
print(type(42)) -- number
print(type("hello")) -- string
print(type({})) -- table
print(type(print)) -- function
print(type(io.stdin)) -- userdata (file handle)
print(type(coroutine.create(function() end))) -- thread
Use type() for conditional logic based on types:
local value = get_some_value()
if type(value) == "string" then
print("Got a string: " .. value)
elseif type(value) == "number" then
print("Got a number: " .. value)
end
Type Coercion
Lua automatically converts between strings and numbers when needed:
-- Arithmetic operations coerce strings to numbers
local result = "10" + 5 -- 15 (string "10" becomes number 10)
local concat = 10 .. 20 -- "1020" (number becomes string)
-- Explicit conversion
local num = tonumber("42") -- 42
local str = tostring(100) -- "100"
-- Common pitfall
print("10" == 10) -- false! Different types
print(tonumber("10") == 10) -- true
Be aware of this when comparing values across types.
Best Practices
- Always use
localunless you explicitly need a global variable - Initialize your variables — uninitialized variables are nil
- Use meaningful names —
countis better thanx - Be aware of type coercion when comparing values
- Use
type()to check types instead of relying on implicit behavior
What’s Next?
Now that you understand variables and types, the next tutorial in this series covers Control Flow in Lua — how to make decisions with if statements and loop with for, while, and repeat.