Storing Dictionary in object is persistent across all objects

Hello, I’m not sure if I am overlooking something or if this is a Roblox issue but trying to store a dictionary within an object causes it to be persistent across all objects of that type.

Module Script

local module = {}
module.__index = module

module.testDictionary = {}
module.testString = ""

function module.new()
	local newModule = {}
	setmetatable(newModule, module)

	return newModule
end

function module:testFunction()
	self.testDictionary['test'] = 1
	self.testString = "String"
end

return module

Server Script

local module = require(game.ServerScriptService.ModuleScript)

local module1 = module.new()
local module2 = module.new()

module1:testFunction()

print(module1.testString)
print(module2.testString)

print(module1.testDictionary)
print(module2.testDictionary)

The expected output is for module1 to have a dictionary with an entry called ‘test’ = 1, and a String called “String”. Both of those variables in module2 should be empty because I only called the function on module1. However the actual result is confusing. The string prints as expected, but the dictionary is persistent across both modules.

Output

  21:44:15.543  String  -  Server - Script:8 -- Module1
  21:44:15.544    -  Server - Script:9 -- Module2
  21:44:15.544   ▼  { -- Module1
                    ["test"] = 1
                 }  -  Server - Script:11
  21:44:15.544   ▼  { -- Module2
                    ["test"] = 1
                 }  -  Server - Script:12

If anyone has any ideas why this might be happening please let me know, thank you!

local module = {}
module.__index = module -- You tell it to look in module for missing keys.

module.testDictionary = {} -- You create "testDictionary" in module.
module.testString = ""

function module.new()
    local newModule = {}
    -- You've set module as the metatable, meaning it will look inside module
    -- for missing keys.
    setmetatable(newModule, module)

    return newModule
end

function module:testFunction()
    -- You access "testDictionary" of self here, which doesn't actually exist,
    -- meaning it looks inside module. You then assign 1 to the key "test" of
    -- module.testDictionary.
    self.testDictionary['test'] = 1
    -- This works as intended because no __newindex was specified on module.
    self.testString = "String"
end

return module

This explains what happens and why it happens.

Thanks for the help, the issue was that the tables created on the original module were references in the new modules rather than being their own instance. This is why the dictionary persisted across modules and not the string. The string being its own instance while the dictionary was just a reference.

Mostly right, but testString works because you’re assigning to it and there is no __newindex handler on module for that behaviour, so it creates the testString on the separate table instead of module.

testDictionary["key"] doesn’t work as expected because to assign to a key, you first need to fetch testDictionary, which does trigger __index which returns module.testDictionary.

1 Like

I understand it now, thank you. I didn’t fully understand Lua metatables and treated then similar to Java objects which threw me off.

It’s all good, a somewhat (definitely not entirely) close comparison is a JavaScript Proxy, this comparison is moreso focused on the ability to assign a function to __index, though, as well as skims over the fact that __index requires the key to not already exist before being invoked.


I highly recommend reading this documentation from Roblox that details specifically what is available in Luau.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.