Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Lua Dictionary
  3. ... (Variadic Arguments)

... (Variadic Arguments)

In Lua, specifying ... (an ellipsis) in the argument list of a function allows it to accept any number of arguments. This page covers the basic usage of ..., the select() function for getting argument counts and accessing specific arguments, and a comparison with table.pack().

Syntax

-- -----------------------------------------------
-- Defining a variadic function
-- -----------------------------------------------
local function f(...)           -- ... receives variadic arguments
    local args = {...}          -- convert to a table with {...}
end

-- -----------------------------------------------
-- Manipulating arguments with select()
-- -----------------------------------------------
select('#', ...)                -- returns the count of arguments (including nil)
select(n, ...)                  -- returns all arguments from the nth onward (1-based)
select(-1, ...)                 -- negative index counts from the end

-- -----------------------------------------------
-- Comparison with table.pack()
-- -----------------------------------------------
table.pack(...)                 -- like {...}, converts to a table but records count in field n
-- Example: local t = table.pack(...)  ->  t.n holds the argument count
-- With {...}, the # operator cannot get the exact count when nil is at the end
-- When nil may be present, use table.pack or select('#', ...)

Syntax Reference

Syntax / FunctionDescription
function f(...)Defines a variadic function by specifying ... in the argument list.
{...}Converts ... to a table. Note that # may not return an accurate count when nil is at the end.
select('#', ...)Returns the exact count of variadic arguments including nil.
select(n, ...)Returns all arguments from the nth onward as multiple values. A negative n counts from the end.
table.pack(...)Converts variadic arguments to a table and records the argument count in field n. Accurately preserves the count even when nil is present.
table.unpack(t, i, j)Expands table elements as multiple values. The counterpart to table.pack().

Sample Code

jjk_varargs.lua
-- jjk_varargs.lua — uses Jujutsu Kaisen character data to demonstrate
-- Lua variadic arguments (...) and select()

-- -----------------------------------------------
-- Basic: convert to a table with {...} and process arguments
-- -----------------------------------------------

-- Function that receives sorcerer names and prints them with numbers
local function list_sorcerers(label, ...)
    local names = {...}                        -- convert variadic args to a table
    print("=== " .. label .. " ===")
    for i = 1, #names do
        print(string.format("  %d. %s", i, names[i]))
    end
    print("")
end

list_sorcerers("Tokyo Jujutsu High",
    "Itadori Yuji", "Fushiguro Megumi", "Kugisaki Nobara", "Gojo Satoru")

list_sorcerers("Kyoto School",
    "Nishimiya Momo", "Zen'in Mai", "Kamo Noritoshi")

-- -----------------------------------------------
-- select('#', ...) to get the argument count
-- -----------------------------------------------

-- Function that calculates the total and average of cursed energy values
-- select('#', ...) gets the exact count even when nil is present
local function calc_cursed_energy(...)
    local count = select('#', ...)   -- get total argument count including nil
    local total = 0
    for i = 1, count do
        local v = select(i, ...)     -- get the i-th argument
        if v then
            total = total + v
        end
    end
    local avg = count > 0 and (total / count) or 0
    print(string.format("  Arg count: %d  Total: %d  Average: %.1f", count, total, avg))
end

print("=== Cursed Energy Aggregation ===")
io.write("Gojo, Geto, Nanami: ")
calc_cursed_energy(3000, 2800, 1500)

io.write("Itadori, Fushiguro, Kugisaki (some nil): ")
calc_cursed_energy(1800, nil, 1200)   -- example with nil
print("")

-- -----------------------------------------------
-- select(n, ...) to get arguments from the nth onward
-- -----------------------------------------------

-- Function that treats the first argument as a header and the rest as data
local function show_mission(header, ...)
    print("[ Mission: " .. header .. " ]")
    local member_count = select('#', ...)   -- get the count of remaining arguments
    for i = 1, member_count do
        local member = select(i, ...)       -- use only the first of the multiple values
        print("  Assigned: " .. tostring(member))
    end
    print("")
end

print("=== Mission Assignments ===")
show_mission("Eliminate S-rank cursed spirit", "Gojo Satoru")
show_mission("Recover Cursed Womb: Death Paintings", "Itadori Yuji", "Fushiguro Megumi", "Kugisaki Nobara")
show_mission("Exchange event with Kyoto", "Itadori Yuji", "Fushiguro Megumi", "Kugisaki Nobara", "Panda", "Inumaki Toge")

-- -----------------------------------------------
-- Comparison with table.pack(): counting arguments that include nil
-- -----------------------------------------------

-- table.pack() accurately stores the count in field n
local function compare_pack(...)
    local t_brace = {...}                    -- table conversion with {...}
    local t_pack  = table.pack(...)          -- table conversion with table.pack()

    print(string.format("  # operator on {...}: %d", #t_brace))
    print(string.format("  table.pack .n:       %d", t_pack.n))
    print(string.format("  select('#', ...):    %d", select('#', ...)))
    print("")
end

print("=== Count Comparison with nil Present ===")
io.write("(\"Gojo Satoru\", nil, \"Geto Suguru\") case:\n")
compare_pack("Gojo Satoru", nil, "Geto Suguru")

io.write("(1, 2, nil, nil) case:\n")
compare_pack(1, 2, nil, nil)
lua jjk_varargs.lua
=== Tokyo Jujutsu High ===
  1. Itadori Yuji
  2. Fushiguro Megumi
  3. Kugisaki Nobara
  4. Gojo Satoru

=== Kyoto School ===
  1. Nishimiya Momo
  2. Zen'in Mai
  3. Kamo Noritoshi

=== Cursed Energy Aggregation ===
Gojo, Geto, Nanami:   Arg count: 3  Total: 7300  Average: 2433.3
Itadori, Fushiguro, Kugisaki (some nil):   Arg count: 3  Total: 3000  Average: 1000.0

=== Mission Assignments ===
[ Mission: Eliminate S-rank cursed spirit ]
  Assigned: Gojo Satoru

[ Mission: Recover Cursed Womb: Death Paintings ]
  Assigned: Itadori Yuji
  Assigned: Fushiguro Megumi
  Assigned: Kugisaki Nobara

[ Mission: Exchange event with Kyoto ]
  Assigned: Itadori Yuji
  Assigned: Fushiguro Megumi
  Assigned: Kugisaki Nobara
  Assigned: Panda
  Assigned: Inumaki Toge

=== Count Comparison with nil Present ===
("Gojo Satoru", nil, "Geto Suguru") case:
  # operator on {...}: 1
  table.pack .n:       3
  select('#', ...):    3

(1, 2, nil, nil) case:
  # operator on {...}: 2
  table.pack .n:       4
  select('#', ...):    4

Common Mistakes

Common Mistake 1: Using # on a table made with {...} to count arguments that include nil

When counting a table made with {...} using #, trailing nil values are not included. Use select('#', ...) or table.pack's .n field to get the exact count of variadic arguments including nil.

ng_example.lua
local function count_args(...)
    local t = {...}
    return #t  -- inaccurate when nil is present
end

print(count_args(1, nil, 3))  -- expected: 3, but...
1

Using select('#', ...) gives the exact count including nil.

ok_example.lua
local function count_args(...)
    return select('#', ...)
end

print(count_args(1, nil, 3))  -- 3
3

Common Mistake 2: Trying to use ... inside a nested closure without capturing it first

... is not a local variable; it is a special syntax usable only directly within the function's own scope. Before passing it to a closure or another function, it must be saved to a table using {...} or table.pack(...).

ng_example2.lua
local function outer(...)
    local inner = function()
        -- trying to capture ... in an inner closure causes an error in Lua 5.2+
        return ...
    end
    return inner()
end

print(outer(1, 2, 3))
lua: ng_example2.lua:3: cannot use '...' outside a vararg function

Save to a table in the outer function first, then pass to the closure.

ok_example2.lua
local function outer(...)
    local args = {...}  -- save to a table
    local inner = function()
        return table.unpack(args)
    end
    return inner()
end

print(outer(1, 2, 3))
1	2	3

Overview

Lua's variadic arguments are received by writing ... in the function definition's argument list. Inside the function body, ... is accessed by converting it to a table with {...} or by going through select(). {...} is convenient but has the pitfall that the # operator does not return an accurate count when nil is at the end. When nil may be present, use select('#', ...) or the .n field of table.pack(...) to verify the count. select(n, ...) returns all arguments from the nth onward as multiple values, making it convenient for patterns where the first argument is a "label" and the rest are treated as data. For related topics, also see Closures and Table Basics.

If you find any errors or copyright issues, please .