SharedTable
Represents a table-like data structure that can be shared across execution contexts. While it can be used for various sorts of general data storage, it is designed especially for use with Parallel Luau, where it can be used to share state across scripts parented under different Actor instances.
There are a couple idiomatic ways to communicate shared tables between scripts. One method is to store and retrieve SharedTable objects in the SharedTableRegistry. The registry lets any script in the same data model get or set a SharedTable by name. Another method is to use Actor:SendMessage() to send a shared table to another Actor inside a message.
Like a Luau table, a SharedTable object stores a set of key-value element pairs. Unlike a Luau table, only selected types of objects may be stored in a SharedTable, similar to other restrictions you'll find elsewhere in the Roblox Engine.
Keys must either be (1) a string or (2) a nonnegative integer number less than 232. Other kinds of keys are not supported.
Values must have one of the following types: Boolean, Number, Vector, String, SharedTable, or a serializable data type. The ability to store a SharedTable as a value in another SharedTable permits the construction of nested and recursive data structures.
SharedTable objects are distinct and different SharedTable objects never compare equal, even if they have contents that would compare equal.
Like a Luau table, a SharedTable object may be frozen, in which case it is read-only. An attempt to modify a frozen SharedTable will raise an error. A frozen SharedTable can be created by first creating a (non-frozen, modifiable) SharedTable with the desired contents, and then calling SharedTable.cloneAndFreeze() to create a frozen clone of it.
Code Samples
Elements in a SharedTable are accessed in the same way as elements of Luau tables, using the st[k] syntax or, for string keys, st.k.
local st = SharedTable.new()
st[1] = "a"
st["x"] = true
st.y = 5
assert(st[1] == "a")
assert(st["x"] == true)
assert(st.x == true)
assert(st["y"] == 5)
assert(st.y == 5)
-- true is not a valid SharedTable key, so attempting to set that key
-- fails:
assert(not pcall(function() st[true] = 100 end))
-- A function is not a valid SharedTable value, so attempting to set a
-- value to a function fails:
assert(not pcall(function() st["f"] = function() end end))
The for loop can be used to iterate over the elements of a SharedTable. The elements of the SharedTable are not iterated directly. Instead, a shallow clone is made of the SharedTable, and its elements are iterated. This is done to ensure that a consistent view of the SharedTable is maintained throughout the iteration. Thus, both of the following for loops have the same behavior.
Note that this means that if the SharedTable is accessed directly from within the body of the iteration loop, its state may not be consistent with the state observed through the iteration.
The iteration order is partially specified. Elements with numeric keys are iterated before elements with string keys. Elements with numeric keys are iterated in ascending numeric order. Elements with string keys are iterated in an unspecified order.
local st = SharedTable.new({"a", "b", "c"})
for k, v in SharedTable.clone(st, false) do
print(k, ": ", v)
end
for k, v in st do
print(k, ": ", v)
end
Summary
Constructors
- new()
Returns a new, empty SharedTable.
Returns a new SharedTable containing elements equivalent to those in the provided Luau table.
Functions
Removes all of the elements from the SharedTable.
Creates and returns a clone of the provided SharedTable.
Creates and returns a frozen (read-only) clone of the provided SharedTable.
Adds delta to the value with the provided key and returns the original value.
Returns true if the SharedTable is frozen (read-only).
Returns the number of elements stored in the SharedTable.
Updates the value with the provided key via the provided update function.
Constructors
new
Returns a new SharedTable containing elements equivalent to those in the provided Luau table.
If the provided Luau table contains any keys or values that cannot be stored in a SharedTable, construction of the SharedTable fails. See the summary at the top of this page for a list of types of objects that can be stored in a SharedTable. If the Luau table contains any table as a value, that table is converted into a new SharedTable.
local t = {}t.x = 1t.y = 2t.z = {"a", "b", "c"}local st = SharedTable.new(t)assert(st.x == 1)assert(st.y == 2)assert(st.z[1] == "a")assert(st.z[2] == "b")assert(st.z[3] == "c")
Note that in some cases it may be desirable to store a SharedTable in the SharedTableRegistry. The Class.ShareTableRegistry:GetSharedTable() method provides a convenient way to accomplish this.
Parameters
The Luau table whose elements are to be stored in the new SharedTable.
Functions
clear
Atomically removes all of the elements from a SharedTable.
If the SharedTable is frozen, the operation fails and an error will be raised.
local st = SharedTable.new({"a", "b", "c"})assert(SharedTable.size(st) == 3)SharedTable.clear(st)assert(SharedTable.size(st) == 0)
Parameters
The SharedTable to clear.
Returns
clone
Creates a clone of a SharedTable and returns the clone.
If the optional deep argument is not present, or if it is present and its value is false, then a shallow clone is created. A shallow clone copies only the top-level SharedTable object. If any value in the SharedTable itself is a SharedTable, then both the original SharedTable and the clone SharedTable will refer to the same SharedTable.
The shallow clone operation is atomic, so the clone SharedTable will contain a consistent snapshot of the state in the original SharedTable, even if it is being modified concurrently from other scripts.
If the optional deep argument is present and its value is true, then a deep clone is created. A deep clone recursively copies a structure of SharedTable objects, such that there is no state shared between the original SharedTable and the clone.
The clone of each SharedTable within the graph of SharedTable objects is atomic, but the deep clone as a whole is not atomic. Thus, the clone of each SharedTable within the graph will contain a consistent snapshot of the state of the original SharedTable object from which it was cloned, but the states of different SharedTable objects may be inconsistent if the graph is being modified concurrently from other scripts.
The SharedTable object(s) being cloned may be frozen (read-only) or not. Regardless, the newly created clones are not frozen (and are thus modifiable). To create frozen clones, use the SharedTable.cloneAndFreeze function.
To illustrate the difference between a shallow clone and a deep clone, consider the following samples. This first sample creates a shallow clone and the second creates a deep clone.
Parameters
The SharedTable object to clone.
Whether to create a deep clone (true) or a shallow clone (false).
Returns
Code Samples
This code sample creates a shallow clone of a SharedTable.
local original = SharedTable.new()
original["a"] = "original a"
original["b"] = "original b"
original["c"] = SharedTable.new()
original["c"]["d"] = "original d"
local clone = SharedTable.clone(original, false)
clone["a"] = "new a"
clone["b"] = "new b"
clone["c"]["d"] = "new d"
assert(original["a"] == "original a")
assert(original["b"] == "original b")
-- Because this was a shallow clone, original["c"] and clone["c"] are
-- the same SharedTable object.
assert(original["c"] == clone["c"])
assert(original["c"]["d"] == "new d")
This code sample creates a deep clone of a SharedTable.
local original = SharedTable.new()
original["a"] = "original a"
original["b"] = "original b"
original["c"] = SharedTable.new()
original["c"]["d"] = "original d"
local clone = SharedTable.clone(original, true)
clone["a"] = "new a"
clone["b"] = "new b"
clone["c"]["d"] = "new d"
assert(original["a"] == "original a")
assert(original["b"] == "original b")
-- Because this was a deep clone, clone["c"] is a clone of original["c"];
-- they are distinct SharedTable objects.
assert(original["c"] ~= clone["c"])
assert(original["c"]["d"] == "original d")
cloneAndFreeze
Creates a frozen (read-only) clone of a SharedTable and returns the clone. The behavior of this function is the same as the behavior of clone, except that the clone is frozen.
If a deep clone is requested, then all of the cloned SharedTable objects are frozen.
Parameters
The SharedTable object to clone.
Whether to create a deep clone (true) or a shallow clone (false).
Returns
increment
Atomically increments the value of an element. An element with the specified key must exist in the SharedTable, and it must be of type number. The specified delta is added to the value, and the original value is returned.
The SharedTable.update function can also be used for this purpose; this increment function exists for convenience and performance (in general, increment is much faster than update, so it should be preferred where possible). The following two function calls have the same effect:
local st = SharedTable.new()
st["x"] = 1
local oldValue = SharedTable.increment(st, "x", 1)
SharedTable.update(st, "x", function(v)
oldValue = v
return v + 1
end)
If the SharedTable is frozen, the operation fails and an error will be raised.
Parameters
The SharedTable object to be updated.
The key of the element in the SharedTable object to be updated.
The value to be added to the element in the SharedTable.
Returns
The original value of the element, before delta was added to it.
Code Samples
This code sample demonstrates usage of SharedTable.increment().
local st = SharedTable.new()
st["x"] = 1
local oldValue = SharedTable.increment(st, "x", 1)
assert(oldValue == 1)
assert(st["x"] == 2)
-- The value of the specified key must be a number. If it is not a
-- number, the call will fail:
st["y"] = "test"
assert(not pcall(function() SharedTable.increment(st, "y", 1) end))
isFrozen
Returns true if the SharedTable is frozen (read-only).
local st1 = SharedTable.new({"a", "b", "c"})assert(not SharedTable.isFrozen(st1))local st2 = SharedTable.cloneAndFreeze(st1)assert(SharedTable.isFrozen(st2))
Parameters
The SharedTable object whose frozen state is to be queried.
Returns
size
Returns the number of elements stored in the SharedTable. Note that if other scripts are concurrently modifying the SharedTable, the returned size may no longer be correct after it is returned, since other scripts may have added or removed elements from the SharedTable.
local st = SharedTable.new({"a", "b", "c"})assert(SharedTable.size(st) == 3)st[2] = nilassert(SharedTable.size(st) == 2)
Parameters
The SharedTable object whose size is to be queried.
Returns
update
Atomically updates the value of an element.
When a SharedTable is accessed concurrently from scripts running in different execution contexts, it is possible for their accesses to interleave unpredictably. Because of this, code like the following is generally incorrect, because the value may have changed between the read on the first line and the update on the second line:
local oldValue = st["x"]st["x"] = oldValue .. ",x"
The update function makes it possible to perform an atomic update to an element. It takes a function that it will call with the current value of the element. The function can then compute and return the new value. Note that the function may be called multiple times if the SharedTable is being concurrently modified from other scripts.
If the SharedTable is frozen, the operation fails and an error will be raised.
Parameters
The SharedTable object to be updated.
The key of the element in the SharedTable object to be updated.
The function that will be called to compute the new value for the element.
Returns
Code Samples
This code sample demonstrates usage of SharedTable.update().
local st = SharedTable.new()
st["x"] = "abcd"
SharedTable.update(st, "x", function(v)
assert(v == "abcd")
return v .. "e"
end)
assert(st["x"] == "abcde")