Functions, Closures, and Varargs
Functions are the building blocks of any Lua program. They let you encapsulate logic, avoid repetition, and structure your code into manageable pieces. In this tutorial, you’ll learn how to define functions, understand closures, work with variable arguments, and apply best practices.
Defining Functions
Lua provides several ways to define functions. The most common syntax uses the function keyword:
function greet(name)
print("Hello, " .. name .. "!")
end
greet("World") -- Output: Hello, World!
You can also assign functions to variables:
local add = function(a, b)
return a + b
end
print(add(3, 5)) -- Output: 8
Both approaches create a function, but the first declares it globally while the second assigns it to a local variable. Prefer local functions—they’re cleaner and avoid polluting the global namespace.
Multiple Return Values
Lua functions can return multiple values, which is particularly useful:
function divide(a, b)
if b == 0 then
return nil, "division by zero"
end
return a / b, nil
end
local result, err = divide(10, 2)
print(result) -- Output: 5
print(err) -- Output: nil
This pattern lets you return both a value and an error indicator, similar to Go’s error handling style.
Closures
A closure is a function that captures variables from its surrounding scope. This powerful feature enables interesting patterns:
function counter()
local count = 0
return function()
count = count + 1
return count
end
end
local c1 = counter()
local c2 = counter()
print(c1()) -- Output: 1
print(c1()) -- Output: 2
print(c2()) -- Output: 1
Each call to counter() creates a new closure with its own count variable. The returned function “remembers” the environment where it was created.
Closures are useful for:
- Creating private variables
- Building stateful functions
- Implementing callbacks and event handlers
Variable Arguments (Varargs)
Lua supports variable arguments using the ... syntax:
function sum(...)
local total = 0
for _, v in ipairs({...}) do
total = total + v
end
return total
end
print(sum(1, 2, 3)) -- Output: 6
print(sum(10, 20, 30, 40)) -- Output: 100
You can also use select to work with arguments without creating a table:
function average(...)
local count = select("#", ...) -- number of arguments
local total = 0
for i = 1, count do
total = total + select(i, ...)
end
return total / count
end
print(average(10, 20, 30)) -- Output: 20
Multiple Assignments with Varargs
When a function returns multiple values, you can capture them directly:
function stats(numbers)
local sum = 0
for _, n in ipairs(numbers) do
sum = sum + n
end
local mean = sum / #numbers
return sum, mean, #numbers
end
local total, average, count = stats({10, 20, 30})
print(total, average, count) -- Output: 60 20 3
Named Returns
Lua lets you return values by name using a table:
function rectangle(w, h)
return {
width = w,
height = h,
area = w * h,
perimeter = 2 * (w + h)
}
end
local r = rectangle(5, 3)
print(r.area) -- Output: 15
print(r.perimeter) -- Output: 16
This pattern improves readability when returning many values.
Best Practices
-
Use local functions — Avoid polluting the global namespace.
-
Keep functions focused — Each function should do one thing well.
-
Validate inputs — Check for nil values and invalid arguments:
function safeDivide(a, b)
assert(type(a) == "number", "first argument must be a number")
assert(type(b) == "number", "second argument must be a number")
assert(b ~= 0, "cannot divide by zero")
return a / b
end
-
Use multiple returns for errors — Return
nilwith an error message instead of throwing exceptions. -
Document with comments — Explain what the function does, its parameters, and return values.
Common Patterns
Callback Functions
function processItems(items, callback)
local results = {}
for i, item in ipairs(items) do
results[i] = callback(item)
end
return results
end
local doubled = processItems({1, 2, 3}, function(x) return x * 2 end)
print(table.concat(doubled, ", ")) -- Output: 2, 4, 6
Function Factories
function multiplier(factor)
return function(x)
return x * factor
end
end
local double = multiplier(2)
local triple = multiplier(3)
print(double(5)) -- Output: 10
print(triple(5)) -- Output: 15
Summary
Functions in Lua are versatile and powerful. Key takeaways:
- Define functions with
function name(args) ... end - Use closures to create stateful, reusable functions
- Handle variable arguments with
...andselect - Return multiple values for clean error handling
- Prefer local functions and validate inputs
In the next tutorial, you’ll learn about Tables: Lua’s Universal Data Structure, which combine arrays, dictionaries, and objects into one flexible type.
See Also
- Variables and Types in Lua — Learn about Lua’s basic data types
- Control Flow: if, for, while, and repeat — Master conditional statements and loops
- — Explore tables, the core data structure in Lua