Search code examples
lua

Capturing rest of returned values from function into list


Given simple function:

function pullEvent()
  return "eventName", "eventArg1", "eventArg2", "eventArg3", "eventArg4"
end

where number of returned values depends on first returned value, how can I capture first returned value into one variable and all remaining into list?

I can imagine something like:

local eventName, { eventArgs } = pullEvent()

but that is invalid syntax.

Way I'm doing it now is:

local event = { pullEvent() }
local eventName, eventArgs = event[1], { table.unpack(event, 2) }

which works, but I'd like to ask, if there is shorter way of doing it. Preferably without creating event variable.


Solution

  • Generally, you need to use functions and/or select to deal with varargs. In your case:

    local function nameAndArgs(head, ...)
        return head, {...}
    end
    

    which you can then use as

    -- eventArgs will be a table
    local eventName, eventArgs = nameAndArgs(pullEvent())
    

    this simple solution assumes that the event args are never nil, otherwise you potentially get a table with holes where trailing nils are stripped; the vararg 1, nil, 2, nil would become the table {[1] = 1, [3] = 2} - in newer Lua versions you could use table.pack to have Lua store the count of the vararg (select("#", ...)) in the n field of the table, allowing exactly restoring it to a vararg using table.unpack(t, 1, t.n).

    However, you most likely don't want to pack tables. All you want is a "switch" based on the event name which assigns the arguments to variables. This might look as follows:

    local function handleEvent(eventName, ...)
        if eventName == "foo" then
            local bar = ...
            -- do something
        elseif eventName == "bar" then
            local foobar, quux = ...
            -- do something
        else
            error"unknown event"
        end
    end
    handleEvent(pullEvent())
    

    or, more elegantly (and potentially more efficiently if you have many handlers, since it leverages an O(1) hash table lookup rather than doing O(n) comparisons on average), using the "dispatch table of functions" pattern:

    local eventHandlers = {
        foo = function(bar)
            -- do something
        end,
        bar = function(foobar, quux)
            -- do something
        end,
    }
    local function handleEvent(eventName, ...)
        return assert(eventHandlers[eventName], "unknown event")(...)
    end
    handleEvent(pullEvent())