Skip to content
AgileLogics
4 min read

Variadic Functions

Prerequisites: Closures

A function whose last parameter is ... accepts any number of extra arguments.

local function sum(...)
  local total = 0
  for _, v in ipairs({...}) do
    total = total + v
  end
  return total
end

print(sum(1, 2, 3))
print(sum())
print(sum(10, 20, 30, 40))
6
0
100

{...} collects the arguments into a table.

Counting arguments, including nil

{...} loses track of nil values (they make holes). Use select or table.pack:

local function describe(...)
  local n = select("#", ...)
  local args = table.pack(...)
  local parts = {}
  for i = 1, args.n do
    parts[#parts + 1] = tostring(args[i])
  end
  return n .. " args: " .. table.concat(parts, ", ")
end

print(describe("a", nil, "c"))
print(select(2, "a", "b", "c"))
3 args: a, nil, c
b	c
  • select("#", ...) counts all arguments, including nils.
  • select(n, ...) returns the arguments from position n onwards.

Forwarding arguments

... can be passed straight to another function — handy for wrappers:

local function logged(name, fn)
  return function(...)
    print("calling " .. name .. " with " .. select("#", ...) .. " args")
    return fn(...)
  end
end

local add = logged("add", function(a, b) return a + b end)
print(add(2, 3))

local function printf(fmt, ...)
  io.write(string.format(fmt, ...))
end
printf("%s scored %d points\n", "Ada", 42)
calling add with 2 args
5
Ada scored 42 points