Lua - Variables & Constants

Overview

Estimated time: 20–25 minutes

Variables are containers for storing data values in Lua. This tutorial covers variable declaration, scoping rules, constants, and best practices for variable naming and usage.

Learning Objectives

  • Understand variable declaration in Lua
  • Master the difference between local and global variables
  • Learn about variable scoping rules
  • Implement constants and read-only values
  • Apply proper naming conventions

Prerequisites

  • Understanding of Lua data types
  • Basic knowledge of Lua syntax

Variable Declaration

In Lua, variables don't need explicit type declarations. You simply assign a value:

-- Basic variable assignment
name = "Alice"
age = 25
height = 5.7
is_student = true

print("Name:", name)
print("Age:", age)
print("Height:", height)
print("Student:", is_student)

Expected Output:

Name:	Alice
Age:	25
Height:	5.7
Student:	true

Local vs Global Variables

Global Variables

Variables declared without local are global by default:

-- Global variables (avoid when possible)
global_counter = 0
user_name = "John"

function increment_counter()
    global_counter = global_counter + 1
end

increment_counter()
print("Global counter:", global_counter)
print("User name:", user_name)

Expected Output:

Global counter:	1
User name:	John

Local Variables (Recommended)

Always prefer local variables for better performance and code organization:

-- Local variables (recommended)
local counter = 0
local name = "Alice"
local items = {"apple", "banana", "orange"}

local function process_items()
    local total = 0
    for i, item in ipairs(items) do
        total = total + 1
        print("Item " .. i .. ":", item)
    end
    return total
end

local item_count = process_items()
print("Total items:", item_count)

Expected Output:

Item 1:	apple
Item 2:	banana
Item 3:	orange
Total items:	3

Variable Scoping

Block Scope

Local variables are limited to their containing block:

local x = 10
print("Outer x:", x)

do
    local x = 20  -- Different variable
    print("Inner x:", x)
    
    local y = 30   -- Only exists in this block
    print("Inner y:", y)
end

print("Outer x again:", x)
-- print("Outer y:", y)  -- Error: y doesn't exist here

Expected Output:

Outer x:	10
Inner x:	20
Inner y:	30
Outer x again:	10

Function Scope

Variables declared in functions are local to that function:

local function calculate_area(length, width)
    local area = length * width  -- Local to this function
    local perimeter = 2 * (length + width)  -- Also local
    
    print("Calculating area...")
    return area, perimeter
end

local rectangle_area, rectangle_perimeter = calculate_area(5, 3)
print("Area:", rectangle_area)
print("Perimeter:", rectangle_perimeter)

-- area and perimeter don't exist outside the function

Expected Output:

Calculating area...
Area:	15
Perimeter:	16

Multiple Assignment

Lua supports multiple assignment in a single statement:

-- Multiple assignment
local a, b, c = 1, 2, 3
print("a, b, c:", a, b, c)

-- Swapping variables
local x, y = 10, 20
print("Before swap - x:", x, "y:", y)
x, y = y, x
print("After swap - x:", x, "y:", y)

-- Unequal assignments
local p, q, r = 100, 200  -- r will be nil
print("p, q, r:", p, q, r)

local s, t = 1, 2, 3, 4  -- Extra values ignored
print("s, t:", s, t)

Expected Output:

a, b, c:	1	2	3
Before swap - x:	10	y:	20
After swap - x:	20	y:	10
p, q, r:	100	200	nil
s, t:	1	2

Constants in Lua

Lua doesn't have built-in constants, but we can simulate them:

Convention-based Constants

-- Convention: use ALL_CAPS for constants
local PI = 3.14159265359
local MAX_USERS = 1000
local DEFAULT_NAME = "Anonymous"
local VERSION = "1.0.0"

print("Pi:", PI)
print("Max users:", MAX_USERS)
print("Default name:", DEFAULT_NAME)
print("Version:", VERSION)

-- Grouped constants
local CONFIG = {
    MAX_RETRIES = 3,
    TIMEOUT = 30,
    DEBUG_MODE = true
}

print("Max retries:", CONFIG.MAX_RETRIES)
print("Timeout:", CONFIG.TIMEOUT)

Expected Output:

Pi:	3.14159265359
Max users:	1000
Default name:	Anonymous
Version:	1.0.0
Max retries:	3
Timeout:	30

Read-only Tables

Create read-only constants using metatables:

-- Read-only constants using metatables
local function make_readonly(table)
    return setmetatable({}, {
        __index = table,
        __newindex = function(t, key, value)
            error("Attempt to modify read-only table")
        end
    })
end

local CONSTANTS = make_readonly({
    PI = 3.14159,
    E = 2.71828,
    GRAVITY = 9.81
})

print("Constants PI:", CONSTANTS.PI)
print("Constants E:", CONSTANTS.E)

-- This would cause an error:
-- CONSTANTS.PI = 3.14  -- Error: Attempt to modify read-only table

Expected Output:

Constants PI:	3.14159
Constants E:	2.71828

Variable Naming Conventions

Good Naming Practices

-- Good variable names (descriptive and clear)
local user_name = "Alice"
local total_score = 1250
local is_game_over = false
local player_position = {x = 10, y = 20}
local max_health_points = 100

-- Function names should be verbs
local function calculate_distance(x1, y1, x2, y2)
    return math.sqrt((x2 - x1)^2 + (y2 - y1)^2)
end

-- Constants in ALL_CAPS
local MAX_PLAYERS = 4
local DEFAULT_LIVES = 3

print("User:", user_name)
print("Score:", total_score)
print("Game over:", is_game_over)

Naming Conventions

  • Variables: snake_case (recommended) or camelCase
  • Constants: ALL_CAPS_WITH_UNDERSCORES
  • Functions: snake_case with verb names
  • Private variables: Prefix with underscore _private_var

Variable Initialization

Initialize variables properly to avoid nil values:

-- Good initialization practices
local counter = 0           -- Initialize numbers to 0
local message = ""          -- Initialize strings to empty
local items = {}            -- Initialize tables to empty table
local is_ready = false      -- Initialize booleans to false/true
local user_data = nil       -- Explicit nil when appropriate

-- Default values for function parameters
local function greet(name, greeting)
    name = name or "Guest"              -- Default name
    greeting = greeting or "Hello"      -- Default greeting
    return greeting .. ", " .. name .. "!"
end

print(greet())                    -- Uses both defaults
print(greet("Alice"))             -- Uses default greeting
print(greet("Bob", "Hi"))         -- Uses provided values

Expected Output:

Hello, Guest!
Hello, Alice!
Hi, Bob!

Practical Examples

Configuration Management

-- Application configuration
local APP_CONFIG = {
    -- Database settings
    DB_HOST = "localhost",
    DB_PORT = 5432,
    DB_NAME = "myapp",
    
    -- Application settings
    MAX_CONNECTIONS = 100,
    TIMEOUT_SECONDS = 30,
    DEBUG_ENABLED = true,
    
    -- Feature flags
    ENABLE_LOGGING = true,
    ENABLE_CACHING = false
}

-- Using configuration
local function connect_database()
    if APP_CONFIG.DEBUG_ENABLED then
        print("Connecting to database...")
        print("Host:", APP_CONFIG.DB_HOST)
        print("Port:", APP_CONFIG.DB_PORT)
        print("Database:", APP_CONFIG.DB_NAME)
    end
    -- Connection logic here
end

connect_database()

State Management

-- Game state management
local game_state = {
    player_name = "",
    current_level = 1,
    score = 0,
    lives = 3,
    is_paused = false
}

local function update_score(points)
    game_state.score = game_state.score + points
    print("Score updated:", game_state.score)
end

local function lose_life()
    game_state.lives = game_state.lives - 1
    if game_state.lives <= 0 then
        print("Game Over!")
    else
        print("Lives remaining:", game_state.lives)
    end
end

-- Example usage
game_state.player_name = "Player1"
update_score(100)
update_score(50)
lose_life()

Expected Output:

Connecting to database...
Host:	localhost
Port:	5432
Database:	myapp
Score updated:	100
Score updated:	150
Lives remaining:	2

Common Pitfalls

  • Global pollution: Always use local unless you specifically need a global variable
  • Typos in variable names: Lua creates new globals for typos instead of errors
  • Uninitialized variables: Variables default to nil, which might not be what you want
  • Scope confusion: Remember that variables are only visible in their declaring scope

Checks for Understanding

  1. What's the difference between local and global variables?
  2. How do you create multiple variables in one statement?
  3. What happens to extra values in multiple assignment?
  4. What's the recommended naming convention for constants?
  5. How can you provide default values for variables?
Show answers
  1. Local variables are declared with local and have limited scope; global variables are accessible everywhere but should be avoided.
  2. Use comma-separated assignment: local a, b, c = 1, 2, 3
  3. Extra values are ignored; missing values become nil
  4. ALL_CAPS_WITH_UNDERSCORES for constants
  5. Use the or operator: name = name or "default"

Next Steps

Now that you understand variables and scoping, you're ready to dive deeper into Lua's data types and learn how to work with different kinds of values effectively.