select
select extracts elements from a variadic argument list. It returns either all arguments after a given position, or the total count of arguments when used with '#'.
Signature
select(index, ...)
Parameters:
index— a number (position), or the string"#"(count mode)...— variadic arguments
Returns: If index is a number, returns all arguments from that position onward. If index is "#", returns the count of arguments.
Counting Arguments
select("#", ...) returns the number of variadic arguments:
select("#", 1, 2, 3) -- 3
select("#", "a", "b", "c") -- 3
select("#") -- 0
select("#", ...) is the standard way to count variadic arguments in Lua. The ... expression does not expose a length directly — calling #... is a syntax error in every Lua version. When you pass zero arguments, select("#") calmly returns 0, so you can safely branch on an empty vararg without guarding against nil first. This makes it the canonical choice for functions that accept optional trailing parameters.
Getting elements by position
When index is a number, select returns all arguments from that position onward:
select(1, "a", "b", "c") -- "a", "b", "c"
select(2, "a", "b", "c") -- "b", "c"
select(3, "a", "b", "c") -- "c"
The positional form of select is most useful inside variadic functions that need to skip a known number of leading arguments before processing the rest. Because select(n, ...) returns a proper multi-value tail, you can forward it directly to another variadic call — Lua will unpack the returned values as separate arguments without any intermediate table allocation. Keep in mind that accessing argument positions past the actual count silently returns nil, which is why pairing select with select("#", ...) for bounds checking is so common.
Negative index
Negative indices count from the end:
select(-1, "a", "b", "c") -- "c"
select(-2, "a", "b", "c") -- "b", "c"
select(-3, "a", "b", "c") -- "a", "b", "c"
Negative indexing lets you reach into the tail of a vararg without knowing its total length ahead of time. This is what makes select(-1, ...) the idiomatic way to grab the last argument — you do not need to count first and then index. If the negative offset reaches past the start (e.g., select(-5, "a", "b")), Lua returns all arguments from the beginning, same as select(1, ...).
Practical Examples
The following patterns show how select replaces manual counting and indexing in everyday Lua functions. Each example leans on a different aspect of the function — counting, tail extraction, skipping, and paired traversal — so you can pick the pattern that matches your own argument-handling needs.
Iterating over variadic arguments
function sum(...)
local total = 0
for i = 1, select("#", ...) do
total = total + select(i, ...)
end
return total
end
sum(1, 2, 3, 4) -- 10
That loop works because select(i, ...) returns a single value when i lies within bounds and you capture it into a scalar assignment. However, iterating one-at-a-time with select is not the most efficient pattern for long argument lists — every call does a linear scan from position 1. For tight loops over many arguments, packing ... into a table with {...} first and then iterating with ipairs usually runs faster. The select-based approach shines when you need positional access without materializing all arguments, for instance when you only need the first three.
Safely accessing the last argument
function last(...)
return select(-1, ...)
end
last(10, 20, 30) -- 30
Using select(-1, ...) is both concise and safe — it correctly returns nil when the vararg is empty, unlike trying to index into a table-packed {...} where t[#t] would error on an empty list. This pattern appears frequently in wrapper functions that decorate the last argument while forwarding the rest untouched.
Ignoring the first argument
function rest(...)
return select(2, ...)
end
rest("first", "second", "third") -- "second", "third"
Stripping the first argument with select(2, ...) is a common Lua idiom in method-like dispatch tables and middleware chains. The returned multi-value can be forwarded directly to another variadic function without an intermediate table, which keeps the call stack lean. Note that select(2) with a single argument quietly returns nothing — not nil — so the caller sees an empty vararg, exactly what you would expect when there is nothing left after the head.
Processing pairs of arguments
function pairs_sum(...)
local total = 0
local n = select("#", ...) / 2
for i = 1, n do
local key, val = select(2 * i - 1, ...)
total = total + val
end
return total
end
pairs_sum("a", 1, "b", 2, "c", 3) -- 6
Relationship to the arg Table
In Lua 5.0 through 5.1, variadic arguments were accessed through the arg pseudo-table, which had numeric indices and an .n field for the count. select was introduced in Lua 5.2 as part of the vararg expression redesign and replaced that pattern entirely. In modern Lua (5.2+), select handles counting, indexing, and tail extraction without any hidden table allocation, making arg obsolete:
-- Old Lua 5.1 style:
function old_sum(...)
local n = arg.n
local total = 0
for i = 1, n do
total = total + arg[i]
end
return total
end
-- Modern Lua 5.3+ style:
function new_sum(...)
local total = 0
for i = 1, select("#", ...) do
total = total + select(i, ...)
end
return total
end
See Also
- /reference/core-functions/ref-print/ — output values
- /reference/core-functions/ref-tostring/ — convert to string
- /reference/core-functions/ref-type/ — get the type of a value