No `next` support for `SharedTable`s

SharedTables have no metatable or metamethod support, which makes sense because functions are bound to the state they’re created in. My solution to this is something of a wrapper table that uses __index and __newindex metamethods to write to the SharedTable. Specifically, all of this allowed me to add the equivalent of Instance.Changed events for a SharedTable that represented a player’s DataStore save data. When I went to implement the __iter metamethod into the so-called wrapper table, I realized that it was not going to be possible to implement performantly, because the next global function does not work on SharedTables. Here is the solution I use instead:

__iter = function(t)
    local raw = t._RAW --this is the sharedtable
	local keys = {}

	return function()
		for i, v in raw do
			if not keys[i] then
				keys[i] = true
				return i, v
			end
		end
	end
end

It works, but it is very inefficient compared to what would be possible if next supported SharedTables. Metaphorically speaking, instead of Luau counting from 1 to 10 and returning all the values 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, it counts 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, returning only new values. Obviously for linear arrays this isn’t a problem; you can alternatively use a for loop with its bound set to the SharedTable’s length. When working with dictionaries, however, this is the only way to currently implement something similar to what next offers.

Expected behavior

next should support SharedTables, or an equivalent function for use with SharedTables should be added.

2 Likes

Found someone to check this out! We’ll update you as soon as we know more.

4 Likes

Hi @Andy_Wirus,

This may not be the most performant option possible, but I am wondering if it may still be a reasonable solution. You could use Luau coroutines to yield values that are being iterated from a SharedTable.

For example:

function mt:__iter()
    return coroutine.wrap(function()
        for k,v in self._RAW do
            coroutine.yield(k,v)
        end
    end)
end

I think that functionally this is what you are after. Although it might not be as fast as if we exposed an equivalent to next for SharedTables.

Here’s a more complete example of the code being used:

local st = SharedTable.new({"a", "b", "c"})
st.key = "Value"

local mt = {}
function mt:__iter()
    return coroutine.wrap(function()
        for k,v in self._RAW do
            coroutine.yield(k,v)
        end
    end)
end
mt.__index = mt

local wrapper = setmetatable({_RAW = st}, mt)
for k,v in wrapper do
    print(k, v)
end
1 Like

Thx for the feedack! We have added this to our backlog to consider. Will come back here with questions or an update in a bit!