Variables and Types in Lua

· 5 min read · Updated March 17, 2026 · beginner
variables types beginner fundamentals

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

  1. Always use local unless you explicitly need a global variable
  2. Initialize your variables — uninitialized variables are nil
  3. Use meaningful namescount is better than x
  4. Be aware of type coercion when comparing values
  5. 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.